#install.packages("knitr")
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
#install.packages("grid")
#install.packages("MLRMPA")
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")
#install.packages("RColorBrewer")
#install.packages("rgdal")
#install.packages("jsonlite")
#install.packages("RColorBrewer")
install.packages("readr")
Error in install.packages : Updating loaded packages
library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
library(normalr)
library(ggcorrplot)
library(leaflet)
library(plotly)
library(RColorBrewer)
library(readr)
#library(jsonlite)
#library(rgdal)
#library(wesanderson)
#library(MLRMPA)
#??src_mysql
my_db <- src_mysql(
dbname = "covid",
host = "localhost",
user = "root",
password = "1234"
)
my_db
src: mysql 8.0.21 [root@localhost:/covid]
tbls: covid_confirmed_yearly, covid_deaths_yearly, covid_recovered_yearly, covid_thailand_updated, covid19_confirmed_global, covid19_deaths_global,
covid19_recovered_global, covidus, data_ethnic, data_gender, data_health, data_lockdown, data_population, data_testing, full_datas, gdp, gdp19, healthranking,
imf-country, owid-covid-data, population, pornhub, sars_2003, us_gdp, us_latlong, world_temp
##import data
df_conf <- tbl(my_db, sql("select * from covid_confirmed_yearly"))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from covid_deaths_yearly"))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from covid_recovered_yearly"))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
There were 14 warnings (use warnings() to see them)
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
[1] "2020-01-22" "2021-01-11"
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
Warning message:
Unknown or uninitialised column: `Country`.
cleanData <- function(data) {
## remove some columns
data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
## convert from wide to long format
data %<>% gather(key=date, value=count, -country)
## convert from character to date
data %<>% mutate(date = date %>% mdy())
## aggregate by country
data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data
data.world <- data %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
View(data)
x <- raw.confirmed
x$confirmed <- x[, ncol(x)]
x %<>% select(c(Country.Region, Province.State, Lat, Long, confirmed)) %>%
mutate(txt=paste0(Country.Region, ' - ', Province.State, ': ', confirmed))
m <- leaflet(width=1200, height=800) %>% addTiles()
# circle marker (units in pixels)
m %<>% addCircleMarkers(x$Long, x$Lat,
# radius=2+log2(x$confirmed),
radius=0.03*sqrt(x$confirmed),
stroke=F,
color='red', fillOpacity=0.3,
label=x$txt)
# world
m
## China
m %>% setView(95, 35, zoom=4)
## Australia and New Zealand
m %>% setView(135, -27, zoom=4)
## US and Canada
m %>% setView(-105, 40, zoom=4)
## Europe
m %>% setView(10, 50, zoom=4)
NA
#rate
Warning message:
Unknown or uninitialised column: `Country`.
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1),
rate.upper = ifelse(is.nan(rate.upper), 0, rate.upper))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1),
rate.lower = ifelse(is.nan(rate.lower), 0, rate.lower))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1),
rate.daily = ifelse(is.nan(rate.daily), 0, rate.daily))
View(data)
## convert from wide to long format
Warning message:
Unknown or uninitialised column: `Country`.
data.long <- data %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
View(data.long)
##Number of case World
Warning message:
Unknown or uninitialised column: `Country`.
world <- filter(data.long,country == 'World')
world
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
ggplot(aes(x=date, y=count)) +
geom_line(aes(color=type)) +
labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
## show two plots side by side
#grid.arrange(plot1, plot2, ncol=2)
plot1

#gly.plot2 <- ggplotly(plot2)
#gly.plot2
## Current Confirmed Cases
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

## a scatter plot with a smoothed line and vertical x-axis labels
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

## convert from wide to long format, for drawing area plots
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
rates.long <- data %>%
select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
rate.upper='Upper bound',
rate.lower = 'Lower bound'))
#View(rates.long)
g <- rates.long %>% filter(country == "World") %>%
ggplot(aes(x = date, y = count, color = type)) +
geom_line() +
labs(title = "World Death Rate (%)") +
xlab("") + ylab("Death Rate (%)")
g

gly.death <- ggplotly(g)
gly.death
## ranking by confirmed cases
data.latest.all <- data %>% filter(date == max(date)) %>%
select(country, date,confirmed, new.confirmed, current.confirmed,
recovered, deaths, new.deaths, death.rate=rate.lower) %>%
mutate(ranking = dense_rank(desc(confirmed))) %>%
arrange(ranking)
View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
[1] "US" "India" "Brazil" "Russia" "United Kingdom" "France" "Turkey" "Italy" "Spain"
[10] "Germany" "Colombia" "Argentina" "Mexico" "Poland" "Iran" "South Africa" "Ukraine" "Peru"
[19] "Netherlands" "Indonesia"
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
Warning message:
Unknown or uninitialised column: `Country`.
mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
current.confirmed=sum(current.confirmed),
recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
mutate(death.rate=(100 * deaths/confirmed) %>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths, current.confirmed,recovered)) %>%
mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
# Add World Population
world_pop <- sum(df_pop$`Population (2020)`)
df_pop[nrow(df_pop) + 1,] = c("World", world_pop)
# Add Other Countries Population
top_pop <- filter(df_pop, df_pop$country %in% top.countries & df_pop$country != "World")
top_pop <- sum(top_pop$`Population (2020)` %>% as.numeric())
others_pop <- (world_pop - top_pop)
df_pop[nrow(df_pop) + 1,] = c("Others", others_pop)
View(df_pop)
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE)
data.latest
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest$population <- data.latest$population %>% as.numeric()
data.latest <- data.latest %>%
select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths,
current.confirmed, recovered, recover.rate, population)) %>%
mutate(confirm.rate = (100 * confirmed / population) %>% round(1))
data.latest
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
NA
## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
confirmed='Total Confirmed',
deaths='Total Deaths',
death.rate='Death Rate (%)',
new.confirmed='New Confirmed (compared with one day before)',
new.deaths='New Deaths (compared with one day before)',
current.confirmed='Current Confirmed',
recover.rate = 'Recover Rate(%)',
confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
| type=='Total Deaths'
| type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Death Rate (%)'
| type=='New Confirmed (compared with one day before)'
| type=='New Deaths (compared with one day before)'
| type=='Recover Rate(%)'
| type=='Confirmed Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

data.two.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

##GDP
df_gdp <- tbl(my_db, sql("select * from gdp"))
df_gdp <- as.data.frame(df_gdp)
df_gdp <- rename(df_gdp,"country"="Real GDP growth (Annual percent change)")
df_gdp <- select(df_gdp,c("country","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021"))
df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
NA
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
View(df_healt)
#Top20Pornhub
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
#temp
df_temp <- tbl(my_db, sql("select * from world_temp"))
df_temp <- as.data.frame(df_temp)
df_temp$Country[df_temp$Country == "United States"] <- "US"
df_city <- select(df_temp,c("Country","City")) %>%
rename(country=Country) %>%
rename(city=City)
numofcity <- aggregate(city ~ country, data = df_city, length)
df_temp <- select(df_temp,c("Country","Avg_Year")) %>%
rename(country=Country)
View(df_temp)
#df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(Avg_Year,na.rm = TRUE)%>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
df_temp
#display.brewer.all()
df_temp.all <- df_temp %>% merge(data.latest.all)
View(df_temp.all)
df_temp_top.all <- df_temp.all %>% filter(country %in% top.countries) %>%
mutate(ranking = ranking - 1) %>%
arrange(ranking)
View(df_temp_top)
g_temp_top <- df_temp_top %>%
ggplot(aes(x = reorder(country, ranking), y = avg_temp, fill = avg_temp)) +
labs(title=paste0("Temperature in Top 20 countries"), subtitle = "Average Temperature in Top 20 countries with most confirmed cases (°C) (2020)") +
scale_color_gradient(low = "#93DBFF", high = "#FF7771") +
geom_text(aes(label=avg_temp, y=avg_temp), size=4, vjust=-0.5) +
geom_bar(stat = "identity", position = "dodge") +
theme(
legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size = 15, hjust = 0.5),
plot.subtitle = element_text(size = 12, hjust = 0.5),
axis.text=element_text(size=8),
axis.text.x=element_text(size = 9, angle=45, hjust=1)) +
scale_x_discrete(name = "Country") +
scale_y_discrete(name = "Average Temperature")
#labs(title = "Temperature in Top 20 countries", subtitle = "Temperature in Top 20 countries with most confirmed cases (°C)")
#g_temp_top
g_temp_top

#df_conf
#data.latest.all
lat.long <- rename(df_conf, "country" = "Country.Region", "city" = "Province.State") %>%
select("country", "Lat", "Long") %>%
merge(df_temp.all[c("country","confirmed", "recovered", "deaths", "avg_temp", "ranking")], by = "country") %>%
distinct(country, .keep_all = TRUE) %>%
mutate(ranking = ranking - 1) %>%
arrange(ranking)
View(lat.long)
label_world <- lat.long
label_world$avg_temp <- as.numeric(label_world[, names(label_world) %in% c("avg_temp")])
label_world <- label_world %>%
mutate(txt=paste0('<b>',ranking, '</b>',
'<br/>','<b>',country, '</b>',
'<br/>', "Temperature: ",avg_temp, ' °C',
'<br/>', "Confirmed: ", confirmed,
'<br/>', "Deaths: ", deaths,
'<br/>', "Recovered: ", recovered
))
label_world$txt <- label_world$txt %>% lapply(htmltools::HTML)
label_world
label_top <- label_world %>% filter(ranking < 21)
label_top
wpal <- colorNumeric("YlOrRd", label_world$avg_temp, n = 4)
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
7: Unknown or uninitialised column: `Country`.
8: Unknown or uninitialised column: `Country`.
topIcon <- makeIcon("star.png",
#iconUrl = "https://static.vecteezy.com/system/resources/previews/001/189/063/non_2x/star-rounded-png.png",
iconWidth = 10, iconHeight = 10
#iconAnchorX = 20, iconAnchorY = 20
)
label_world <- label_world %>% filter(ranking > 20)
m <- leaflet(width=1200, height=800) %>% addTiles()
m %<>% addCircleMarkers(label_world$Long, label_world$Lat,
# radius=2+log2(x$confirmed),
radius=10,#*log2(m.world$avg.temp),
stroke=F,
#color='red',
color = wpal(label_world$avg_temp),
fillOpacity=0.5,
#popup=label.top$txt
label= label_world$txt,
group = "World"
) %>%
addCircleMarkers(label_top$Long, label_top$Lat,
# radius=2+log2(x$confirmed),
radius=10,#*log2(m.world$avg.temp),
stroke=F,
#color='red',
color = wpal(label_top$avg_temp),
fillOpacity=0.5,
#popup=label.top$txt
label= label_top$txt,
group = "Top 20 Countries"
) %>%
addLabelOnlyMarkers(label_top$Long, label_top$Lat, label = label_top$ranking,
labelOptions = labelOptions(noHide = TRUE, textOnly = TRUE,
direction = "head",
offset = c(5,4)),
group = "Top 20 Countries") %>%
addLegend("bottomright", pal = wpal, values = label_world$avg_temp, opacity = 1,
labFormat = labelFormat(suffix = " °C"),
title = "Temperature") %>%
addLayersControl(
#baseGroups = c("OSM (default)", "Toner", "Toner Lite"),
overlayGroups = c("Top 20 Countries", "World"),
options = layersControlOptions(collapsed = FALSE)
)
m
NA
#Top 20 with gdp
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World'& country!='Others')
#data.top <- head(data.top,20)
View(data.top)
data.gdp <- filter(data.longGDP,year=='2020')
View(data.gdp)
#rank GDP
data.top.hight <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(desc(GDP)))
#data.top.hight
k <- 20
top.gdp <- data.top.hight %>%
filter(ranking < k) %>%
arrange(ranking)
data.top.low <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(GDP))
low.gdp.long <- data.top.low %>%
filter(ranking < k) %>%
arrange(ranking)
View(low.gdp.long)
top.gdp
low.gdp
world.gdp <- df_gdp %>% filter(country == "World") %>%
gather(key = Year, value = GDP, -country)
world.gdp
world.gdp$group <- cut(world.gdp$GDP, c(-Inf,0, Inf), labels = c("-","+"))
g <- ggplot(world.gdp, aes(x = Year, y = GDP, fill = group)) +
geom_text(aes(label=GDP, y = GDP), size=3, vjust=-1) +
labs(title=paste0("Real GDP growth (Annual percent change) of World from 2012-2021"))+
geom_bar(stat = "identity")+
scale_fill_manual(values = c("-" = "red","+" = "limegreen"))+
theme_bw() + theme(legend.position = "none")+
xlab("")+
theme(#legend.title=element_blank(),
plot.title = element_text(size=7),
axis.text=element_text(size=8))
g

gly.world.gdp <- ggplotly(g)
gly.world.gdp
gdp.top20 <- df_gdp2019 %>%
There were 11 warnings (use warnings() to see them)
select(c("rank", "country", "GDP (millions of US dollars)")) %>%
merge(data.latest.all %>%
select(country, ranking, confirmed, recovered, deaths) %>%
filter(country %in% top.countries & country != "World"), by = "country") %>%
arrange(ranking) %>%
mutate(ranking = ranking - 1)
gdp.top20 %<>% rename("GDP" = "GDP (millions of US dollars)")
gdp.top20
g <- ggplot(gdp.top20, aes(x = GDP, y = reorder(country, -ranking))) +
geom_histogram(stat = "identity", aes(fill = GDP))+
scale_fill_gradient("GDP", low = "#FF4038", high = "#50E952") +
labs(title=paste0("GDP of Top 20 Countries in 2019 (millions of US dollars)")) +
geom_text(aes(label=GDP, x = GDP), size=3.5, hjust=-0.2) +
xlab("GDP (millions of US dollars)") +
ylab("") +
theme(legend.title=element_blank())
Ignoring unknown parameters: binwidth, bins, pad
g

gly.top.gdp <- ggplotly(g)
gly.top.gdp
df_sars <- tbl(my_db, sql("select * from sars_2003"))
df_sars <- as.data.frame(df_sars)
df_sars
dates.s <- df_sars[,1]%>% mdy()
range(dates.s)
[1] "2003-03-17" "2003-07-11"
min.date.s <- min(dates.s)
max.date.s <- max(dates.s)
min.date.txt.s <- min.date.s %>% format('%d %b %Y')
max.date.txt.s <- max.date.s %>% format('%d %b %Y')
# clean data Sars
data.sars <- df_sars %>% rename(c("date" = "Date", "confirmed" = "Cumulative_number" ,"deaths" = "Number_deaths", "recovered" = "Number_recovered")) %>%
mutate(date = date %>% mdy()) %>%
group_by(country, date) %>% as.data.frame()
View(data.sars)
# Add World's Sars cases
world.sars <- data.sars %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data.sars %<>% rbind(world.sars)
data.sars %<>% mutate(current.confirmed = confirmed - deaths - recovered)
#View(world.sars)
#View(data.sars)
#rate
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
data.sars %<>% arrange(country, date)
n <- nrow(data.sars)
day1.sars <- min(data.sars$date)
data.sars %<>% mutate(new.confirmed = ifelse(date == day1.sars, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1.sars, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1.sars, NA, recovered - lag(recovered, n=1)))
data.sars %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data.sars %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1),
rate.upper = ifelse(is.nan(rate.upper), 0, rate.upper))
## lower bound: death rate based on total confirmed cases
data.sars %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1),
rate.lower = ifelse(is.nan(rate.lower), 0, rate.lower))
## death rate based on the number of death/recovered on every single day
data.sars %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1),
rate.daily = ifelse(is.nan(rate.daily), 0, rate.daily))
View(data.sars)
## convert from wide to long format
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
data.sars.long <- data.sars %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.sars.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
View(data.sars.long)
# World sars' long data
world.sars.long <- data.sars.long %>%
filter(country == "World")
View(world.sars.long)
g <- ggplot(world.sars.long, aes(date, count, color = type)) +
geom_line()+
labs(title = "Number of Cases Worldwide: SARs")+
xlab("")+
ylab("")
g

gly.g <- ggplotly(g)
gly.g
gly.plot2
## Current Confirmed Cases
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
data.sars.world <- data.sars %>% filter(country=='World')
View(data.sars.world)
n <- nrow(data.sars.world)
View(data.sars.world)
plot1 <- ggplot(data.sars.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)
Error in FUN(X[[i]], ...) : object 'new.confirmed' not found

## a scatter plot with a smoothed line and vertical x-axis labels
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
plot1 <- ggplot(data.sars.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.sars.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.sars.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.sars.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

#df_thai <- tbl(my_db, sql("select * from covid_thailand_updated"))
Warning message:
Unknown or uninitialised column: `Country`.
#df_thai <- as.data.frame(df_thai)
df_thai <- read_csv("D:/4D-2/Project 2/Data/covid_thailand_updated.csv")
-- Column specification --------------------------------------------------------
cols(
No. = col_double(),
announce_date = col_character(),
notification_date = col_character(),
sex = col_character(),
age = col_double(),
nationality = col_character(),
province_of_isolation = col_character(),
province_of_onset = col_character(),
district_of_onset = col_character(),
risk = col_character()
)
Sys.setlocale("LC_CTYPE", "Thai")
[1] "Thai_Thailand.874"
options(encoding = "UTF-8")
#View(df_thai)
dates.th <- df_thai[,2]%>% mdy()
All formats failed to parse. No formats found.
range(dates.th)
[1] NA NA
min.date.th <- min(dates.th)
max.date.th <- max(dates.th)
min.date.txt.th <- min.date.th %>% format('%d %b %Y')
max.date.txt.th <- max.date.th %>% format('%d %b %Y')
df_thai$announce_date <- mdy(df_thai$announce_date)
df_thai$notification_date <- mdy(df_thai$notification_date)
df_thai
df_thai <- df_thai %>% select(!No.) %>%
group_by(announce_date)
df_thai
# Total confirmed cases in Thailand
Warning message:
Unknown or uninitialised column: `Country`.
data.thai.count <- df_thai %>%
select(announce_date) %>%
summarise(comfirmed = n()) %>% as.data.frame()
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.count
data.thai.count$cumulative_confirmed <- cumsum(data.thai.count[, 2])
data.thai.count
g.th <- ggplot(data.thai.count) +
geom_line(aes(x = announce_date, y = cumulative_confirmed)) +
labs(title = "Thai Confirmed Cases (Jan 2020 - Jan 2021)") +
xlab("Date (Announce Date)") +
ylab("Confirmed Cases")
g.th

gly.th <- ggplotly(g.th)
gly.th
# Confirmed cases divided by sex (gender)
data.thai.gender <- df_thai %>%
group_by(sex) %>%
summarise(count = n()) %>%
mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
#mutate(pos = cumsum(percent) - 0.5*percent) %>%
arrange(desc(percent))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.gender
data.thai.gender$sex <- factor(data.thai.gender$sex, levels = as.character(data.thai.gender$sex))
data.thai.gender$sex
[1] หญิง ชาย <NA>
Levels: หญิง ชาย
g.th.gender <- data.thai.gender %>%
ggplot(aes(x = "", y = percent, fill = sex)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y") +
theme_void() +
geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
guides(fill = guide_legend(reverse = TRUE))
g.th.gender

# Confirmed cases divided by age
data.thai.age <- df_thai %>%
group_by(age) %>%
summarise(count = n()) %>%
arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.age
# Confirmed cases divided by nationality
data.thai.nationality <- df_thai %>%
group_by(nationality) %>%
summarise(count = n()) %>%
arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.nationality
# Data in US
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
df_us <- tbl(my_db, sql("select * from covidus"))
Error in tbl(my_db, sql("select * from covidus")) :
could not find function "tbl"
View(us.total.long)
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
g1 <- data.us.long %>% filter(state == "US") %>%
ggplot(aes(x = date, y = count, color = type)) +
geom_line() +
labs(title = paste0("Cumulative cases in US : ", min.date.txt.us, '-', max.date.txt.us, " (Log Scale)")) +
scale_y_continuous(trans='log10')
g2 <- data.us.long %>% filter(state == "US") %>%
ggplot(aes(x = date, y = count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title = paste0("Cumulative cases in US : ", min.date.txt.us, '-', max.date.txt.us, " (Log Scale)")) +
scale_y_continuous(trans='log10')+
scale_fill_manual(values=c('red', 'black'))+
ylab("")
g3 <- us.total.long %>%
filter(type %in% c("new.confirmed")) %>%
ggplot(aes(x = date, y = count, color = type)) +
geom_line() +
labs(title = paste0("Daily confirmed cases in US : ", min.date.txt.us, '-', max.date.txt.us)) +
xlab("Date") +
ylab("Confirmed cases")
#g1
#g2
#g3
#gly.us1 <- ggplotly(g1)
gly.us2 <- ggplotly(g2)
Transformation introduced infinite values in continuous y-axis
gly.us3 <- ggplotly(g3)
#gly.us1
gly.us2
gly.us3
day1.us <- min(data.us$date)
Warning message:
Unknown or uninitialised column: `Country`.
data.us %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)))
data.us %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths))
View(data.us)
#data.us.daily <- data.us %>% filter(state == "US")
#View(data.us.daily)
data.us.pop <- df_us_pop %>% select(State, Population) %>%
rename("state" = "State")
#data.us.pop
data.us.gender <- df_us_gender %>% select(State, Male, Female) %>%
rename("state" = "State")
data.us.gender
data.us.latest <- data.us %>%
filter(date == max.date.us) %>%
merge(data.us.pop, by = "state", all.x = T) %>%
merge(data.us.gender, by = "state", all.x = T)
#View(data.us.latest)
data.us.latest$Population[data.us.latest$state == "US"] <- sum(data.us.pop$Population)
data.us.latest %<>% mutate(ranking = dense_rank(desc(confirmed)),
confirmed.rate = (100 * confirmed / Population) %>% round(2),
death.rate = (100 * deaths / confirmed) %>% round(2),
Male.confirmed = (Male * confirmed) %>% round(0),
Female.confirmed = Female * confirmed %>% round(0),
Male.deaths = Male * deaths,
Female.deaths = Female * deaths) %>%
arrange(ranking)
top.us <- data.us.latest[,1]
top.us
[1] "US" "California" "Texas" "Florida" "Illinois"
[6] "New York" "Ohio" "Georgia" "Pennsylvania" "Tennessee"
[11] "Michigan" "Wisconsin" "North Carolina" "Indiana" "Arizona"
[16] "New Jersey" "Minnesota" "Missouri" "Massachusetts" "Alabama"
[21] "Virginia" "Colorado" "Louisiana" "South Carolina" "Iowa"
[26] "Oklahoma" "Maryland" "Utah" "Kentucky" "Washington"
[31] "Kansas" "Nevada" "Arkansas" "Mississippi" "Connecticut"
[36] "Nebraska" "Idaho" "New Mexico" "Oregon" "Puerto Rico"
[41] "South Dakota" "North Dakota" "Rhode Island" "Montana" "West Virginia"
[46] "Delaware" "Alaska" "Wyoming" "New Hampshire" "District of Columbia"
[51] "Maine" "Hawaii" "Guam" "Vermont" "Virgin Islands"
[56] "Northern Mariana Islands"
data.us.latest
NA
NA
# List of top 20 state
k <- 20
data.us.top <- data.us.latest %>%
filter(ranking <= k+1) %>%
arrange(ranking)
us.state.top <- data.us.top %>% pull(state) %>% as.character()
us.state.top %>% setdiff('US') %>% print()
[1] "California" "Texas" "Florida" "Illinois" "New York" "Ohio" "Georgia" "Pennsylvania" "Tennessee"
[10] "Michigan" "Wisconsin" "North Carolina" "Indiana" "Arizona" "New Jersey" "Minnesota" "Missouri" "Massachusetts"
[19] "Alabama" "Virginia"
# confirmed rate & death rate of top 20 state
g.rate <- data.us.latest %>% filter(state %in% us.state.top & state != "US") %>%
select(state, confirmed.rate, death.rate, ranking) %>%
gather(key = Type, value = Percent, -c(state, ranking)) %>%
ggplot(aes(x=reorder(state, -desc(ranking)), y=Percent, fill = Percent)) +
geom_bar(stat='identity') +
scale_fill_gradient(low = "#ebbc62", high = "#b42006") +
geom_text(aes(label=Percent, y=Percent), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Confirmed Rate & Death Rate of Top 20 State in US')) +
#scale_fill_continuous(name='State', labels=aes(Percent)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~Type, ncol=1, scales='free_y')
g.rate

us.confirmed.num <- data.us.top$confirmed[data.us.top$state == "US"] %>% as.numeric()
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
7: Unknown or uninitialised column: `Country`.
8: Unknown or uninitialised column: `Country`.
9: Unknown or uninitialised column: `Country`.
us.confirmed.num
[1] 18706036
data.us.top %<>% mutate(confirmed.per.us = (confirmed * 100 / us.confirmed.num) %>% round(1))
data.us.top
g.us.top1 <- data.us.top %>%
filter(state != "US") %>%
select(state, confirmed, confirmed.per.us, ranking) %>%
gather(key = Type, value = count, -c(state, ranking, confirmed.per.us)) %>%
ggplot(aes(fill = count, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "dodge", stat = "identity") +
labs(title = "20 state in US with most confirmed cases") +
scale_fill_gradient(low = "#ebbc62", high = "#b42006") +
xlab("") +
ylab("Confirmed Cases") +
geom_text(aes(label=paste0(confirmed.per.us, "%")), size=3, vjust=-0.5) +
theme(axis.text.x = element_text(angle = 90,vjust = 0.5))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top2 <- data.us.top %>%
filter(state != "US") %>%
select(state, Male.confirmed, Female.confirmed, Male.deaths, Female.deaths, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(fill = Type, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "stack", stat = "identity") +
labs(title = "20 most confirmed cases's state in US (Divided by gender)") +
xlab("") +
ylab("Cases") +
theme(axis.text.x = element_text(angle = 45,vjust = 1)) +
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top3 <- data.us.top %>%
filter(state != "US") %>%
select(state, confirmed.per.us, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(x = "", y = confirmed.per.us, fill = confirmed.per.us)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y") +
theme_void() +
geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
guides(fill = guide_legend(reverse = TRUE))
g.us.top1

gly.us.top1 <- ggplotly(g.us.top1)
gly.us.top1
gly.us.top2 <- ggplotly(g.us.top2)
gly.us.top2
#gly.us.top3 <- ggplotly(g.us.top3)
#gly.us.top3
g.us.top1 <- data.us.top %>%
filter(state != "US") %>%
select(state, confirmed, deaths, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(fill = Type, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "dodge", stat = "identity") +
labs(title = "20 state in US with most confirmed cases") +
xlab("") +
ylab("Confirmed Cases") +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top2 <- data.us.top %>%
filter(state != "US") %>%
select(state, Male, Female, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(fill = Type, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "stack", stat = "identity") +
labs(title = "Gender ratio of 20 most confirmed cases's state in US") +
xlab("") +
ylab("Cases") +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top3 <- data.us.top %>%
filter(state != "US") %>%
select(state, Male.confirmed, Female.confirmed, Male.deaths, Female.deaths, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(fill = Type, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "stack", stat = "identity") +
labs(title = "20 most confirmed cases's state in US (Divided by gender)") +
xlab("") +
ylab("Cases") +
theme(axis.text.x = element_text(angle = 90,vjust = 1)) +
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top3

gly.us.top1 <- ggplotly(g.us.top1)
gly.us.top1
gly.us.top2 <- ggplotly(g.us.top2)
gly.us.top2
gly.us.top3 <- ggplotly(g.us.top3)
gly.us.top3
NA
mergcountry = function(data1,data2){
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
data <- merge(x = data1, y = data2, by = "country", all.x = TRUE)
return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>%
select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>%
rename(GDP="GDP (millions of US dollars)")
Error in merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>% :
could not find function "%>%"
#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate, healthcare, avg_temp, Pornhub))
head(corr_data)
cor(corr_data)
ggcorrplot(cor(corr_data),hc.order = TRUE,
outline.color = "white",
colors = c("#6D9EC1","white","#E46726"),
lab = TRUE)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KI2luc3RhbGwucGFja2FnZXMoIk1MUk1QQSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHByZXAiKQ0KI2luc3RhbGwucGFja2FnZXMoIm5vcm1hbHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdnY29ycnBsb3QiKQ0KI2luc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpDQojaW5zdGFsbC5wYWNrYWdlcygicmdkYWwiKQ0KI2luc3RhbGwucGFja2FnZXMoImpzb25saXRlIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KaW5zdGFsbC5wYWNrYWdlcygicmVhZHIiKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KG5vcm1hbHIpDQpsaWJyYXJ5KGdnY29ycnBsb3QpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShyZWFkcikNCiNsaWJyYXJ5KGpzb25saXRlKQ0KDQojbGlicmFyeShyZ2RhbCkNCiNsaWJyYXJ5KHdlc2FuZGVyc29uKQ0KI2xpYnJhcnkoTUxSTVBBKQ0KDQojPz9zcmNfbXlzcWwNCm15X2RiIDwtIHNyY19teXNxbCgNCiAgZGJuYW1lID0gImNvdmlkIiwNCiAgaG9zdCA9ICJsb2NhbGhvc3QiLA0KICB1c2VyID0gInJvb3QiLA0KICBwYXNzd29yZCA9ICIxMjM0Ig0KKQ0KbXlfZGINCg0KIyNpbXBvcnQgZGF0YQ0KZGZfY29uZiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZF9jb25maXJtZWRfeWVhcmx5IikpDQpkZl9jb25mIDwtIGFzLmRhdGEuZnJhbWUoZGZfY29uZikNCmRmX2NvbmYNCmRmX2RlYXRocyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZF9kZWF0aHNfeWVhcmx5IikpDQpkZl9kZWF0aHMgPC0gYXMuZGF0YS5mcmFtZShkZl9kZWF0aHMpDQpkZl9kZWF0aHMNCmRmX3JlY292ZXIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWRfcmVjb3ZlcmVkX3llYXJseSIpKQ0KZGZfcmVjb3ZlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3JlY292ZXIpDQpkZl9yZWNvdmVyDQpgYGANCmBgYHtyfQ0KIyNjaGVjayB0aGUgdGltZSBmcmFtZSBvZiB0aGUgZGF0YQ0Kbi5jb2wgPC0gbmNvbChkZl9jb25mKQ0KZGF0ZXMgPC0gbmFtZXMoZGZfY29uZilbNTpuLmNvbF0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzKQ0KbWluLmRhdGUgPC0gbWluKGRhdGVzKQ0KbWF4LmRhdGUgPC0gbWF4KGRhdGVzKQ0KbWluLmRhdGUudHh0IDwtIG1pbi5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dCA8LSBtYXguZGF0ZSAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpgYGANCmBgYHtyfQ0KI2NsZWFuIGRhdGENCmNsZWFuRGF0YSA8LSBmdW5jdGlvbihkYXRhKSB7DQogICMjIHJlbW92ZSBzb21lIGNvbHVtbnMNCiAgZGF0YSAlPD4lIHNlbGVjdCgtYyhQcm92aW5jZS5TdGF0ZSwgTGF0LCBMb25nKSkgJT4lIHJlbmFtZShjb3VudHJ5PUNvdW50cnkuUmVnaW9uKQ0KICAjIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KICBkYXRhICU8PiUgZ2F0aGVyKGtleT1kYXRlLCB2YWx1ZT1jb3VudCwgLWNvdW50cnkpDQogICMjIGNvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gZGF0ZQ0KICBkYXRhICU8PiUgbXV0YXRlKGRhdGUgPSBkYXRlICU+JSBtZHkoKSkNCiAgIyMgYWdncmVnYXRlIGJ5IGNvdW50cnkNCiAgZGF0YSAlPD4lIGdyb3VwX2J5KGNvdW50cnksIGRhdGUpICU+JSBzdW1tYXJpc2UoY291bnQ9c3VtKGNvdW50LCBuYS5ybT1UKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KICByZXR1cm4oZGF0YSkNCn0NCiMjIGNsZWFuIHRoZSB0aHJlZSBkYXRhIHNldHMNCmRhdGEuY29uZmlybWVkIDwtIGRmX2NvbmYgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoY29uZmlybWVkPWNvdW50KQ0KZGF0YS5kZWF0aHMgPC0gZGZfZGVhdGhzICU+JSBjbGVhbkRhdGEoKSAlPiUgcmVuYW1lKGRlYXRocz1jb3VudCkNCmRhdGEucmVjb3ZlcmVkIDwtIGRmX3JlY292ZXIgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUocmVjb3ZlcmVkPWNvdW50KQ0KZGF0YSA8LSBkYXRhLmNvbmZpcm1lZCAlPiUgbWVyZ2UoZGF0YS5kZWF0aHMsIGFsbD1UKSAlPiUgbWVyZ2UoZGF0YS5yZWNvdmVyZWQsIGFsbD1UKQ0KZGF0YQ0KIyMgY291bnRyaWVzL3JlZ2lvbnMgd2l0aCBjb25maXJtZWQgY2FzZXMsIGV4Y2wuIGNydWlzZSBzaGlwcw0KY291bnRyaWVzIDwtIGRhdGEgJT4lIHB1bGwoY291bnRyeSkgJT4lIHNldGRpZmYoJ0NydWlzZSBTaGlwJykNCmRhdGENCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudHJ5PSdXb3JsZCcsDQogICAgICAgICAgICBjb25maXJtZWQgPSBzdW0oY29uZmlybWVkLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIGRlYXRocyA9IHN1bShkZWF0aHMsIG5hLnJtPVQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkID0gc3VtKHJlY292ZXJlZCwgbmEucm09VCkpDQpkYXRhICU8PiUgcmJpbmQoZGF0YS53b3JsZCkNCmRhdGENCmRhdGEgJTw+JSBtdXRhdGUoY3VycmVudC5jb25maXJtZWQgPSBjb25maXJtZWQgLSBkZWF0aHMgLSByZWNvdmVyZWQpDQpWaWV3KGRhdGEpDQpgYGANCmBgYHtyfQ0KeCA8LSByYXcuY29uZmlybWVkDQp4JGNvbmZpcm1lZCA8LSB4WywgbmNvbCh4KV0NCnggJTw+JSBzZWxlY3QoYyhDb3VudHJ5LlJlZ2lvbiwgUHJvdmluY2UuU3RhdGUsIExhdCwgTG9uZywgY29uZmlybWVkKSkgJT4lDQptdXRhdGUodHh0PXBhc3RlMChDb3VudHJ5LlJlZ2lvbiwgJyAtICcsIFByb3ZpbmNlLlN0YXRlLCAnOiAnLCBjb25maXJtZWQpKQ0KbSA8LSBsZWFmbGV0KHdpZHRoPTEyMDAsIGhlaWdodD04MDApICU+JSBhZGRUaWxlcygpDQojIGNpcmNsZSBtYXJrZXIgKHVuaXRzIGluIHBpeGVscykNCm0gJTw+JSBhZGRDaXJjbGVNYXJrZXJzKHgkTG9uZywgeCRMYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAjIHJhZGl1cz0yK2xvZzIoeCRjb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmFkaXVzPTAuMDMqc3FydCh4JGNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgICAgICAgICBzdHJva2U9RiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSdyZWQnLCBmaWxsT3BhY2l0eT0wLjMsDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD14JHR4dCkNCiMgd29ybGQNCm0NCmBgYA0KYGBge3J9DQojIyBDaGluYQ0KbSAlPiUgc2V0Vmlldyg5NSwgMzUsIHpvb209NCkNCiMjIEF1c3RyYWxpYSBhbmQgTmV3IFplYWxhbmQNCm0gJT4lIHNldFZpZXcoMTM1LCAtMjcsIHpvb209NCkNCiMjIFVTIGFuZCBDYW5hZGENCm0gJT4lIHNldFZpZXcoLTEwNSwgNDAsIHpvb209NCkNCiMjIEV1cm9wZQ0KbSAlPiUgc2V0VmlldygxMCwgNTAsIHpvb209NCkNCg0KYGBgDQoNCg0KYGBge3J9DQojcmF0ZQ0KZGF0YSAlPD4lIGFycmFuZ2UoY291bnRyeSwgZGF0ZSkNCm4gPC0gbnJvdyhkYXRhKQ0KZGF5MSA8LSBtaW4oZGF0YSRkYXRlKQ0KZGF0YSAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIGNvbmZpcm1lZCAtIGxhZyhjb25maXJtZWQsIG49MSkpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIGRlYXRocyAtIGxhZyhkZWF0aHMsIG49MSkpLA0KICAgICAgICAgICAgICAgICBuZXcucmVjb3ZlcmVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIHJlY292ZXJlZCAtIGxhZyhyZWNvdmVyZWQsIG49MSkpKQ0KZGF0YSAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKG5ldy5jb25maXJtZWQgPCAwLCAwLCBuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShuZXcuZGVhdGhzIDwgMCwgMCwgbmV3LmRlYXRocyksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UobmV3LnJlY292ZXJlZCA8IDAsIDAsIG5ldy5yZWNvdmVyZWQpKQ0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0b3RhbCBkZWF0aHMgYW5kIHJlY292ZXJlZCBjYXNlcw0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLnVwcGVyID0gKDEwMCAqIGRlYXRocyAvIChkZWF0aHMgKyByZWNvdmVyZWQpKSAlPiUgcm91bmQoMSksDQogICAgICAgICAgICAgICAgIHJhdGUudXBwZXIgPSBpZmVsc2UoaXMubmFuKHJhdGUudXBwZXIpLCAwLCByYXRlLnVwcGVyKSkNCg0KIyMgbG93ZXIgYm91bmQ6IGRlYXRoIHJhdGUgYmFzZWQgb24gdG90YWwgY29uZmlybWVkIGNhc2VzDQpkYXRhICU8PiUgbXV0YXRlKHJhdGUubG93ZXIgPSAoMTAwICogZGVhdGhzIC8gY29uZmlybWVkKSAlPiUgcm91bmQoMSksDQogICAgICAgICAgICAgICAgIHJhdGUubG93ZXIgPSBpZmVsc2UoaXMubmFuKHJhdGUubG93ZXIpLCAwLCByYXRlLmxvd2VyKSkNCg0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGRlYXRoL3JlY292ZXJlZCBvbiBldmVyeSBzaW5nbGUgZGF5DQpkYXRhICU8PiUgbXV0YXRlKHJhdGUuZGFpbHkgPSAoMTAwICogbmV3LmRlYXRocyAvIChuZXcuZGVhdGhzICsgbmV3LnJlY292ZXJlZCkpICU+JSByb3VuZCgxKSwNCiAgICAgICAgICAgICAgICAgcmF0ZS5kYWlseSA9IGlmZWxzZShpcy5uYW4ocmF0ZS5kYWlseSksIDAsIHJhdGUuZGFpbHkpKQ0KDQpWaWV3KGRhdGEpDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KZGF0YS5sb25nIDwtIGRhdGEgJT4lDQogIHNlbGVjdChjKGNvdW50cnksIGRhdGUsIGNvbmZpcm1lZCwgY3VycmVudC5jb25maXJtZWQsIHJlY292ZXJlZCwgZGVhdGhzKSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQ9J0N1cnJlbnQgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3ZlcmVkPSdSZWNvdmVyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aHM9J0RlYXRocycpKQ0KVmlldyhkYXRhLmxvbmcpDQpgYGANCg0KYGBge3J9DQojI051bWJlciBvZiBjYXNlIFdvcmxkDQp3b3JsZCA8LSBmaWx0ZXIoZGF0YS5sb25nLGNvdW50cnkgPT0gJ1dvcmxkJykNCndvcmxkDQpwbG90MSA8LSB3b3JsZCAlPiUgZmlsdGVyKHR5cGUgIT0gJ1RvdGFsIENvbmZpcm1lZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCkpICsNCiAgZ2VvbV9hcmVhKGFlcyhmaWxsPXR5cGUpLCBhbHBoYT0wLjUpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSB3b3JsZCAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQpKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9dHlwZSkpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIChsb2cgc2NhbGUpIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3B1cnBsZScsICdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC4yLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NiksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KI2dyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCg0KcGxvdDENCg0KI2dseS5wbG90MiA8LSBnZ3Bsb3RseShwbG90MikNCiNnbHkucGxvdDINCmBgYA0KDQpgYGB7cn0NCiMjIEN1cnJlbnQgQ29uZmlybWVkIENhc2VzDQpkYXRhLndvcmxkIDwtIGRhdGEgJT4lIGZpbHRlcihjb3VudHJ5PT0nV29ybGQnKQ0KbiA8LSBucm93KGRhdGEud29ybGQpDQpwbG90MSA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1jdXJyZW50LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0N1cnJlbnQgQ29uZmlybWVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0RhaWx5IE5ldyBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCmBgYA0KYGBge3J9DQojIyBhIHNjYXR0ZXIgcGxvdCB3aXRoIGEgc21vb3RoZWQgbGluZSBhbmQgdmVydGljYWwgeC1heGlzIGxhYmVscw0KcGxvdDEgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9ZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PXJlY292ZXJlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBSZWNvdmVyZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MyA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3Q0IDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdOZXcgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyBmb3VyIHBsb3RzIHRvZ2V0aGVyLCB3aXRoIDIgcGxvdHMgaW4gZWFjaCByb3cNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIHBsb3QzLCBwbG90NCwgbnJvdz0yKQ0KDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KcmF0ZXMubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCByYXRlLnVwcGVyLCByYXRlLmxvd2VyLCByYXRlLmRhaWx5KSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCnJhdGVzLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIHJhdGUuZGFpbHk9J0RhaWx5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZS51cHBlcj0nVXBwZXIgYm91bmQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYXRlLmxvd2VyID0gJ0xvd2VyIGJvdW5kJykpIA0KI1ZpZXcocmF0ZXMubG9uZykgDQoNCmcgPC0gcmF0ZXMubG9uZyAlPiUgZmlsdGVyKGNvdW50cnkgPT0gIldvcmxkIikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY291bnQsIGNvbG9yID0gdHlwZSkpICsgDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJXb3JsZCBEZWF0aCBSYXRlICglKSIpICsNCiAgeGxhYigiIikgKyB5bGFiKCJEZWF0aCBSYXRlICglKSIpDQoNCmcNCg0KZ2x5LmRlYXRoIDwtIGdncGxvdGx5KGcpDQpnbHkuZGVhdGgNCmBgYA0KDQpgYGB7cn0NCiMjIHJhbmtpbmcgYnkgY29uZmlybWVkIGNhc2VzDQpkYXRhLmxhdGVzdC5hbGwgPC0gZGF0YSAlPiUgZmlsdGVyKGRhdGUgPT0gbWF4KGRhdGUpKSAlPiUNCiAgc2VsZWN0KGNvdW50cnksIGRhdGUsY29uZmlybWVkLCBuZXcuY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwNCiAgICAgICAgIHJlY292ZXJlZCwgZGVhdGhzLCBuZXcuZGVhdGhzLCBkZWF0aC5yYXRlPXJhdGUubG93ZXIpICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhjb25maXJtZWQpKSkgJT4lDQogIGFycmFuZ2UocmFua2luZykNClZpZXcoZGF0YS5sYXRlc3QuYWxsKQ0KDQprIDwtIDIwDQojIyB0b3AgMjAgY291bnRyaWVzOiAyMSBpbmNsLiAnV29ybGQnDQp0b3AuY291bnRyaWVzIDwtIGRhdGEubGF0ZXN0LmFsbCAlPiUgZmlsdGVyKHJhbmtpbmcgPD0gayArIDEpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpICU+JSBwdWxsKGNvdW50cnkpICU+JSBhcy5jaGFyYWN0ZXIoKQ0KdG9wLmNvdW50cmllcyAlPiUgc2V0ZGlmZignV29ybGQnKSAlPiUgcHJpbnQoKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YS5sYXRlc3QgPC0gZGF0YS5sYXRlc3QuYWxsICU+JSBmaWx0ZXIoIWlzLm5hKGNvdW50cnkpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9aWZlbHNlKHJhbmtpbmcgPD0gayArIDEsIGFzLmNoYXJhY3Rlcihjb3VudHJ5KSwgJ090aGVycycpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMsICdPdGhlcnMnKSkpDQpkYXRhLmxhdGVzdCAlPD4lIGdyb3VwX2J5KGNvdW50cnkpICU+JQ0KICBzdW1tYXJpc2UoY29uZmlybWVkPXN1bShjb25maXJtZWQpLCBuZXcuY29uZmlybWVkPXN1bShuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPXN1bShjdXJyZW50LmNvbmZpcm1lZCksDQogICAgICAgICAgICByZWNvdmVyZWQ9c3VtKHJlY292ZXJlZCksIGRlYXRocz1zdW0oZGVhdGhzKSwgbmV3LmRlYXRocz1zdW0obmV3LmRlYXRocykpICU+JQ0KICBtdXRhdGUoZGVhdGgucmF0ZT0oMTAwICogZGVhdGhzL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKSANCmRhdGEubGF0ZXN0DQpkYXRhLmxhdGVzdCAlPD4lIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLCBjdXJyZW50LmNvbmZpcm1lZCxyZWNvdmVyZWQpKSAlPiUNCiAgbXV0YXRlKHJlY292ZXIucmF0ZT0oMTAwICogcmVjb3ZlcmVkL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCg0KZGZfcG9wIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHBvcHVsYXRpb24gIikpDQpkZl9wb3AgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3ApDQpkZl9wb3AgPC0gcmVuYW1lKGRmX3BvcCwiY291bnRyeSI9IkNvdW50cnkiKQ0KDQojIEFkZCBXb3JsZCBQb3B1bGF0aW9uDQp3b3JsZF9wb3AgPC0gc3VtKGRmX3BvcCRgUG9wdWxhdGlvbiAoMjAyMClgKQ0KZGZfcG9wW25yb3coZGZfcG9wKSArIDEsXSA9IGMoIldvcmxkIiwgd29ybGRfcG9wKQ0KDQojIEFkZCBPdGhlciBDb3VudHJpZXMgUG9wdWxhdGlvbg0KdG9wX3BvcCA8LSBmaWx0ZXIoZGZfcG9wLCBkZl9wb3AkY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMgJiBkZl9wb3AkY291bnRyeSAhPSAiV29ybGQiKQ0KdG9wX3BvcCA8LSBzdW0odG9wX3BvcCRgUG9wdWxhdGlvbiAoMjAyMClgICU+JSBhcy5udW1lcmljKCkpDQpvdGhlcnNfcG9wIDwtICh3b3JsZF9wb3AgLSB0b3BfcG9wKSANCmRmX3BvcFtucm93KGRmX3BvcCkgKyAxLF0gPSBjKCJPdGhlcnMiLCBvdGhlcnNfcG9wKQ0KVmlldyhkZl9wb3ApDQoNCmRhdGEubGF0ZXN0IDwtIG1lcmdlKHggPSBkYXRhLmxhdGVzdCwgeSA9IGRmX3BvcCwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQpkYXRhLmxhdGVzdA0KZGF0YS5sYXRlc3QgPC0gcmVuYW1lKGRhdGEubGF0ZXN0LCJwb3B1bGF0aW9uIiA9ICJQb3B1bGF0aW9uICgyMDIwKSIpDQpkYXRhLmxhdGVzdCRwb3B1bGF0aW9uIDwtIGRhdGEubGF0ZXN0JHBvcHVsYXRpb24gJT4lIGFzLm51bWVyaWMoKQ0KZGF0YS5sYXRlc3QgIDwtIGRhdGEubGF0ZXN0ICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBjb25maXJtZWQsIGRlYXRocywgZGVhdGgucmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQsIHJlY292ZXJlZCwgcmVjb3Zlci5yYXRlLCBwb3B1bGF0aW9uKSkgJT4lDQogIG11dGF0ZShjb25maXJtLnJhdGUgPSAoMTAwICogY29uZmlybWVkIC8gcG9wdWxhdGlvbikgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCmBgYA0KYGBge3J9DQpkYXRhLmxhdGVzdCAlPiUgbXV0YXRlKGRlYXRoLnJhdGU9ZGVhdGgucmF0ZSAlPiUgZm9ybWF0KG5zbWFsbD0xKSAlPiUgcGFzdGUwKCclJykpDQoNCmBgYA0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0LCBmb3IgZHJhd2luZyBhcmVhIHBsb3RzDQpkYXRhLmxhdGVzdC5sb25nIDwtIGRhdGEubGF0ZXN0ICU+JSBmaWx0ZXIoY291bnRyeSE9J1dvcmxkJykgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jb3VudHJ5KQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIHdpdGggcHJvcGVyIHRleHQgYW5kIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLmxhdGVzdC5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlybWVkPSdUb3RhbCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdUb3RhbCBEZWF0aHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGgucmF0ZT0nRGVhdGggUmF0ZSAoJSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZD0nTmV3IENvbmZpcm1lZCAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmRlYXRocz0nTmV3IERlYXRocyAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQ9J0N1cnJlbnQgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXIucmF0ZSA9ICdSZWNvdmVyIFJhdGUoJSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlybS5yYXRlID0gJ0NvbmZpcm1lZCBSYXRlKCUpJykpDQojVmlldyhkYXRhLmxhdGVzdC5sb25nKQ0KZGF0YS5vbmUuZGVtIDwtIGZpbHRlcihkYXRhLmxhdGVzdC5sb25nLHR5cGU9PSdUb3RhbCBDb25maXJtZWQnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J1RvdGFsIERlYXRocycNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nQ3VycmVudCBDb25maXJtZWQnKQ0KZGF0YS50d28uZGVtIDwtIGZpbHRlcihkYXRhLmxhdGVzdC5sb25nLHR5cGU9PSdEZWF0aCBSYXRlICglKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nTmV3IENvbmZpcm1lZCAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdSZWNvdmVyIFJhdGUoJSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J0NvbmZpcm1lZCBSYXRlKCUpJykNCmRhdGEudHdvLmRlbQ0KYGBgDQoNCmBgYHtyfQ0KIyMgYmFyIGNoYXJ0DQpkYXRhLm9uZS5kZW0gJT4lIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT0zLCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKQ0KDQpgYGANCmBgYHtyfQ0KDQpkYXRhLnR3by5kZW0gJT4lIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT0zLCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKQ0KYGBgDQoNCmBgYHtyfQ0KIyNHRFANCmRmX2dkcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBnZHAiKSkNCmRmX2dkcCA8LSBhcy5kYXRhLmZyYW1lKGRmX2dkcCkNCmRmX2dkcCA8LSByZW5hbWUoZGZfZ2RwLCJjb3VudHJ5Ij0iUmVhbCBHRFAgZ3Jvd3RoIChBbm51YWwgcGVyY2VudCBjaGFuZ2UpIikNCmRmX2dkcCA8LSBzZWxlY3QoZGZfZ2RwLGMoImNvdW50cnkiLCIyMDEyIiwiMjAxMyIsIjIwMTQiLCIyMDE1IiwiMjAxNiIsIjIwMTciLCIyMDE4IiwiMjAxOSIsIjIwMjAiLCIyMDIxIikpDQpkZl9nZHANCmRmX2dkcDIwMTkgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZ2RwMTkiKSkNCmRmX2dkcDIwMTkgPC0gYXMuZGF0YS5mcmFtZShkZl9nZHAyMDE5KQ0KZGZfZ2RwMjAxOQ0KDQpgYGANCmBgYHtyfQ0KI2hlYWx0aHJhbmtpbmcNCmRmX2hlYWx0IDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGhlYWx0aHJhbmtpbmciKSkNCmRmX2hlYWx0IDwtIGFzLmRhdGEuZnJhbWUoZGZfaGVhbHQpDQpkZl9oZWFsdCA8LSBzZWxlY3QoZGZfaGVhbHQsYygiY291bnRyeSIsImhlYWx0aENhcmVJbmRleCIpKQ0KVmlldyhkZl9oZWFsdCkNCmBgYA0KDQpgYGB7cn0NCiNUb3AyMFBvcm5odWINCmRmX3Bvcm5odWIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gUG9ybmh1YiIpKQ0KZGZfcG9ybmh1YiA8LSBhcy5kYXRhLmZyYW1lKGRmX3Bvcm5odWIpDQpkZl9wb3JuaHViDQpgYGANCg0KYGBge3J9DQojdGVtcA0KZGZfdGVtcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSB3b3JsZF90ZW1wIikpDQpkZl90ZW1wIDwtIGFzLmRhdGEuZnJhbWUoZGZfdGVtcCkgDQpkZl90ZW1wJENvdW50cnlbZGZfdGVtcCRDb3VudHJ5ID09ICJVbml0ZWQgU3RhdGVzIl0gPC0gIlVTIg0KDQpkZl9jaXR5IDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJDaXR5IikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KSAlPiUgDQogIHJlbmFtZShjaXR5PUNpdHkpDQoNCm51bW9mY2l0eSA8LSBhZ2dyZWdhdGUoY2l0eSB+IGNvdW50cnksIGRhdGEgPSBkZl9jaXR5LCBsZW5ndGgpDQoNCmRmX3RlbXAgPC0gc2VsZWN0KGRmX3RlbXAsYygiQ291bnRyeSIsIkF2Z19ZZWFyIikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KQ0KVmlldyhkZl90ZW1wKQ0KDQojZGZfdGVtcCA8LSBkYXRhLmZyYW1lKGNvdW50cnk9ZGZfdGVtcFssMV0sYXZnPXJvd01lYW5zKGRmX3RlbXBbLC0xXSkpDQpkZl90ZW1wIDwtIGRmX3RlbXAgJTw+JSBncm91cF9ieShjb3VudHJ5KSAlPiUgc3VtbWFyaXNlKGF2Z190ZW1wID0gbWVhbihBdmdfWWVhcixuYS5ybSA9IFRSVUUpJT4lIHJvdW5kKDEpKQ0KZGZfdGVtcA0KYGBgDQoNCmBgYHtyfQ0KI2Rpc3BsYXkuYnJld2VyLmFsbCgpDQpkZl90ZW1wLmFsbCA8LSBkZl90ZW1wICU+JSBtZXJnZShkYXRhLmxhdGVzdC5hbGwpDQpWaWV3KGRmX3RlbXAuYWxsKQ0KZGZfdGVtcF90b3AuYWxsIDwtIGRmX3RlbXAuYWxsICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMpICU+JQ0KICBtdXRhdGUocmFua2luZyA9IHJhbmtpbmcgLSAxKSAlPiUNCiAgYXJyYW5nZShyYW5raW5nKQ0KVmlldyhkZl90ZW1wX3RvcCkNCmdfdGVtcF90b3AgPC0gZGZfdGVtcF90b3AgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoY291bnRyeSwgcmFua2luZyksIHkgPSBhdmdfdGVtcCwgZmlsbCA9IGF2Z190ZW1wKSkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgiVGVtcGVyYXR1cmUgaW4gVG9wICAyMCBjb3VudHJpZXMiKSwgc3VidGl0bGUgPSAiQXZlcmFnZSBUZW1wZXJhdHVyZSBpbiBUb3AgMjAgY291bnRyaWVzIHdpdGggbW9zdCBjb25maXJtZWQgY2FzZXMgKMKwQykgKDIwMjApIikgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAiIzkzREJGRiIsIGhpZ2ggPSAiI0ZGNzc3MSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1hdmdfdGVtcCwgeT1hdmdfdGVtcCksIHNpemU9NCwgdmp1c3Q9LTAuNSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHRoZW1lKA0KICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplID0gMTUsIGhqdXN0ID0gMC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGhqdXN0ID0gMC41KSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZSA9IDksIGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiQ291bnRyeSIpICsNCiAgc2NhbGVfeV9kaXNjcmV0ZShuYW1lID0gIkF2ZXJhZ2UgVGVtcGVyYXR1cmUiKQ0KDQogICAgICAgICAgICNsYWJzKHRpdGxlID0gIlRlbXBlcmF0dXJlIGluIFRvcCAgMjAgY291bnRyaWVzIiwgc3VidGl0bGUgPSAiVGVtcGVyYXR1cmUgaW4gVG9wIDIwIGNvdW50cmllcyB3aXRoIG1vc3QgY29uZmlybWVkIGNhc2VzICjCsEMpIikNCiAgICAgICAgIA0KI2dfdGVtcF90b3AgICAgICAgICANCmdfdGVtcF90b3AgDQpgYGANCg0KDQpgYGB7cn0NCiNkZl9jb25mDQojZGF0YS5sYXRlc3QuYWxsDQpsYXQubG9uZyA8LSByZW5hbWUoZGZfY29uZiwgImNvdW50cnkiID0gIkNvdW50cnkuUmVnaW9uIiwgImNpdHkiID0gIlByb3ZpbmNlLlN0YXRlIikgJT4lIA0KICBzZWxlY3QoImNvdW50cnkiLCAiTGF0IiwgIkxvbmciKSAlPiUgDQogIG1lcmdlKGRmX3RlbXAuYWxsW2MoImNvdW50cnkiLCJjb25maXJtZWQiLCAicmVjb3ZlcmVkIiwgImRlYXRocyIsICJhdmdfdGVtcCIsICJyYW5raW5nIildLCBieSA9ICJjb3VudHJ5IikgJT4lDQogIGRpc3RpbmN0KGNvdW50cnksIC5rZWVwX2FsbCA9IFRSVUUpICU+JQ0KICBtdXRhdGUocmFua2luZyA9IHJhbmtpbmcgLSAxKSAlPiUNCiAgYXJyYW5nZShyYW5raW5nKQ0KVmlldyhsYXQubG9uZykNCmBgYA0KDQpgYGB7cn0NCmxhYmVsX3dvcmxkIDwtIGxhdC5sb25nIA0KbGFiZWxfd29ybGQkYXZnX3RlbXAgPC0gYXMubnVtZXJpYyhsYWJlbF93b3JsZFssIG5hbWVzKGxhYmVsX3dvcmxkKSAlaW4lIGMoImF2Z190ZW1wIildKQ0KbGFiZWxfd29ybGQgPC0gbGFiZWxfd29ybGQgJT4lICANCiAgbXV0YXRlKHR4dD1wYXN0ZTAoJzxiPicscmFua2luZywgJzwvYj4nLA0KICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLCc8Yj4nLGNvdW50cnksICc8L2I+JywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIlRlbXBlcmF0dXJlOiAgIixhdmdfdGVtcCwgJyDCsEMnLA0KICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLCAiQ29uZmlybWVkOiAgIiwgY29uZmlybWVkLCANCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIkRlYXRoczogIiwgZGVhdGhzLA0KICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLCAiUmVjb3ZlcmVkOiAiLCByZWNvdmVyZWQNCiAgICAgICAgICAgICAgICAgICAgKSkgDQoNCmxhYmVsX3dvcmxkJHR4dCA8LSBsYWJlbF93b3JsZCR0eHQgJT4lIGxhcHBseShodG1sdG9vbHM6OkhUTUwpDQpsYWJlbF93b3JsZCANCg0KbGFiZWxfdG9wIDwtIGxhYmVsX3dvcmxkICU+JSBmaWx0ZXIocmFua2luZyA8IDIxKQ0KbGFiZWxfdG9wDQpgYGANCg0KYGBge3J9DQp3cGFsIDwtIGNvbG9yTnVtZXJpYygiWWxPclJkIiwgbGFiZWxfd29ybGQkYXZnX3RlbXAsIG4gPSA0KQ0KDQp0b3BJY29uIDwtIG1ha2VJY29uKCJzdGFyLnBuZyIsDQogICNpY29uVXJsID0gImh0dHBzOi8vc3RhdGljLnZlY3RlZXp5LmNvbS9zeXN0ZW0vcmVzb3VyY2VzL3ByZXZpZXdzLzAwMS8xODkvMDYzL25vbl8yeC9zdGFyLXJvdW5kZWQtcG5nLnBuZyIsDQogIGljb25XaWR0aCA9IDEwLCBpY29uSGVpZ2h0ID0gMTANCiAgI2ljb25BbmNob3JYID0gMjAsIGljb25BbmNob3JZID0gMjANCiAgDQopDQoNCmxhYmVsX3dvcmxkIDwtIGxhYmVsX3dvcmxkICU+JSBmaWx0ZXIocmFua2luZyA+IDIwKSANCiAgDQptIDwtIGxlYWZsZXQod2lkdGg9MTIwMCwgaGVpZ2h0PTgwMCkgJT4lIGFkZFRpbGVzKCkgIA0KbSAlPD4lICBhZGRDaXJjbGVNYXJrZXJzKGxhYmVsX3dvcmxkJExvbmcsIGxhYmVsX3dvcmxkJExhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICMgcmFkaXVzPTIrbG9nMih4JGNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgICAgICAgICByYWRpdXM9MTAsIypsb2cyKG0ud29ybGQkYXZnLnRlbXApLA0KICAgICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlPUYsDQogICAgICAgICAgICAgICAgICAgICAgICAjY29sb3I9J3JlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHdwYWwobGFiZWxfd29ybGQkYXZnX3RlbXApLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5PTAuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICNwb3B1cD1sYWJlbC50b3AkdHh0DQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD0gbGFiZWxfd29ybGQkdHh0LA0KICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiV29ybGQiDQogICAgICAgICAgICAgICAgICAgICAgICApICU+JQ0KICANCiAgYWRkQ2lyY2xlTWFya2VycyhsYWJlbF90b3AkTG9uZywgbGFiZWxfdG9wJExhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICMgcmFkaXVzPTIrbG9nMih4JGNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgICAgICAgICByYWRpdXM9MTAsIypsb2cyKG0ud29ybGQkYXZnLnRlbXApLA0KICAgICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlPUYsDQogICAgICAgICAgICAgICAgICAgICAgICAjY29sb3I9J3JlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHdwYWwobGFiZWxfdG9wJGF2Z190ZW1wKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eT0wLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAjcG9wdXA9bGFiZWwudG9wJHR4dA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9IGxhYmVsX3RvcCR0eHQsDQogICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9ICJUb3AgMjAgQ291bnRyaWVzIg0KICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgDQogIGFkZExhYmVsT25seU1hcmtlcnMobGFiZWxfdG9wJExvbmcsIGxhYmVsX3RvcCRMYXQsIGxhYmVsID0gbGFiZWxfdG9wJHJhbmtpbmcsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxPcHRpb25zID0gbGFiZWxPcHRpb25zKG5vSGlkZSA9IFRSVUUsIHRleHRPbmx5ID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJoZWFkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IGMoNSw0KSksDQogICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiVG9wIDIwIENvdW50cmllcyIpICU+JQ0KICANCiAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbCA9IHdwYWwsIHZhbHVlcyA9IGxhYmVsX3dvcmxkJGF2Z190ZW1wLCBvcGFjaXR5ID0gMSwNCiAgICAgICAgICAgIGxhYkZvcm1hdCA9IGxhYmVsRm9ybWF0KHN1ZmZpeCA9ICIgwrBDIiksDQogICAgICAgICAgICB0aXRsZSA9ICJUZW1wZXJhdHVyZSIpICU+JSANCiAgDQogIGFkZExheWVyc0NvbnRyb2woDQogICAgI2Jhc2VHcm91cHMgPSBjKCJPU00gKGRlZmF1bHQpIiwgIlRvbmVyIiwgIlRvbmVyIExpdGUiKSwNCiAgICBvdmVybGF5R3JvdXBzID0gYygiVG9wIDIwIENvdW50cmllcyIsICJXb3JsZCIpLA0KICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkNCiAgKQ0KbQ0KDQpgYGANCg0KDQoNCg0KYGBge3J9DQojVG9wIDIwIHdpdGggZ2RwDQpkYXRhLmxvbmdHRFAgPC0gZGZfZ2RwICU+JSBnYXRoZXIoa2V5PXllYXIsIHZhbHVlPUdEUCwgLWMoY291bnRyeSkpDQpkYXRhLnRvcCA8LSBkYXRhLmxhdGVzdCAlPiUgZmlsdGVyKGNvdW50cnkhPSdXb3JsZCcmIGNvdW50cnkhPSdPdGhlcnMnKQ0KI2RhdGEudG9wIDwtIGhlYWQoZGF0YS50b3AsMjApDQpWaWV3KGRhdGEudG9wKQ0KZGF0YS5nZHAgPC0gZmlsdGVyKGRhdGEubG9uZ0dEUCx5ZWFyPT0nMjAyMCcpDQpWaWV3KGRhdGEuZ2RwKQ0KYGBgDQoNCmBgYHtyfQ0KI3JhbmsgR0RQDQpkYXRhLnRvcC5oaWdodCA8LSBkYXRhLmdkcCAlPiUgc2VsZWN0KGNvdW50cnksIHllYXIsR0RQKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKGRlc2MoR0RQKSkpDQojZGF0YS50b3AuaGlnaHQNCg0KayA8LSAyMA0KDQp0b3AuZ2RwIDwtIGRhdGEudG9wLmhpZ2h0ICU+JSANCiAgZmlsdGVyKHJhbmtpbmcgPCBrKSAlPiUgDQogIGFycmFuZ2UocmFua2luZykNCg0KDQpkYXRhLnRvcC5sb3cgPC0gZGF0YS5nZHAgJT4lIHNlbGVjdChjb3VudHJ5LCB5ZWFyLEdEUCkgJT4lDQogIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhHRFApKQ0KbG93LmdkcC5sb25nIDwtIGRhdGEudG9wLmxvdyAlPiUgDQogIGZpbHRlcihyYW5raW5nIDwgaykgJT4lIA0KICBhcnJhbmdlKHJhbmtpbmcpDQpWaWV3KGxvdy5nZHAubG9uZykNCg0KDQp0b3AuZ2RwDQpsb3cuZ2RwDQpgYGANCg0KYGBge3J9DQoNCndvcmxkLmdkcCA8LSBkZl9nZHAgJT4lIGZpbHRlcihjb3VudHJ5ID09ICJXb3JsZCIpICU+JQ0KICBnYXRoZXIoa2V5ID0gWWVhciwgdmFsdWUgPSBHRFAsIC1jb3VudHJ5KQ0Kd29ybGQuZ2RwDQoNCndvcmxkLmdkcCRncm91cCA8LSBjdXQod29ybGQuZ2RwJEdEUCwgYygtSW5mLDAsIEluZiksIGxhYmVscyA9IGMoIi0iLCIrIikpDQoNCmcgPC0gZ2dwbG90KHdvcmxkLmdkcCwgYWVzKHggPSBZZWFyLCB5ID0gR0RQLCBmaWxsID0gZ3JvdXApKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1HRFAsIHkgPSBHRFApLCBzaXplPTMsIHZqdXN0PS0xKSArDQogICAgbGFicyh0aXRsZT1wYXN0ZTAoIlJlYWwgR0RQIGdyb3d0aCAoQW5udWFsIHBlcmNlbnQgY2hhbmdlKSBvZiBXb3JsZCBmcm9tIDIwMTItMjAyMSIpKSsNCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiLSIgPSAicmVkIiwiKyIgPSAibGltZWdyZWVuIikpKw0KICAgIHRoZW1lX2J3KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKw0KICAgIHhsYWIoIiIpKw0KICAgIHRoZW1lKCNsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpDQoNCmcNCg0KZ2x5LndvcmxkLmdkcCA8LSBnZ3Bsb3RseShnKQ0KZ2x5LndvcmxkLmdkcA0KYGBgDQpgYGB7cn0NCmdkcC50b3AyMCA8LSBkZl9nZHAyMDE5ICU+JQ0KICBzZWxlY3QoYygicmFuayIsICJjb3VudHJ5IiwgIkdEUCAobWlsbGlvbnMgb2YgVVMgZG9sbGFycykiKSkgJT4lDQogIG1lcmdlKGRhdGEubGF0ZXN0LmFsbCAlPiUgDQogICAgICAgICAgc2VsZWN0KGNvdW50cnksIHJhbmtpbmcsIGNvbmZpcm1lZCwgcmVjb3ZlcmVkLCBkZWF0aHMpICU+JSANCiAgICAgICAgICBmaWx0ZXIoY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMgJiBjb3VudHJ5ICE9ICJXb3JsZCIpLCBieSA9ICJjb3VudHJ5IikgJT4lDQogIGFycmFuZ2UocmFua2luZykgJT4lIA0KICBtdXRhdGUocmFua2luZyA9IHJhbmtpbmcgLSAxKSANCmdkcC50b3AyMCAlPD4lIHJlbmFtZSgiR0RQIiA9ICJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikNCmdkcC50b3AyMA0KDQpnIDwtIGdncGxvdChnZHAudG9wMjAsIGFlcyh4ID0gR0RQLCB5ID0gcmVvcmRlcihjb3VudHJ5LCAtcmFua2luZykpKSArDQogIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiaWRlbnRpdHkiLCBhZXMoZmlsbCA9IEdEUCkpKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgiR0RQIiwgbG93ID0gIiNGRjQwMzgiLCBoaWdoID0gIiM1MEU5NTIiKSArIA0KICBsYWJzKHRpdGxlPXBhc3RlMCgiR0RQICBvZiBUb3AgMjAgQ291bnRyaWVzIGluIDIwMTkgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1HRFAsIHggPSBHRFApLCBzaXplPTMuNSwgaGp1c3Q9LTAuMikgKw0KICB4bGFiKCJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpDQpnDQoNCmdseS50b3AuZ2RwIDwtIGdncGxvdGx5KGcpDQpnbHkudG9wLmdkcA0KYGBgDQoNCg0KYGBge3J9DQpkZl9zYXJzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHNhcnNfMjAwMyIpKQ0KZGZfc2FycyA8LSBhcy5kYXRhLmZyYW1lKGRmX3NhcnMpDQpkZl9zYXJzDQoNCmRhdGVzLnMgPC0gZGZfc2Fyc1ssMV0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzLnMpDQptaW4uZGF0ZS5zIDwtIG1pbihkYXRlcy5zKQ0KbWF4LmRhdGUucyA8LSBtYXgoZGF0ZXMucykNCm1pbi5kYXRlLnR4dC5zIDwtIG1pbi5kYXRlLnMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0LnMgPC0gbWF4LmRhdGUucyAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQoNCmBgYA0KDQpgYGB7cn0NCiMgY2xlYW4gZGF0YSBTYXJzIA0KZGF0YS5zYXJzIDwtIGRmX3NhcnMgJT4lIHJlbmFtZShjKCJkYXRlIiA9ICJEYXRlIiwgImNvbmZpcm1lZCIgPSAiQ3VtdWxhdGl2ZV9udW1iZXIiICAsImRlYXRocyIgPSAiTnVtYmVyX2RlYXRocyIsICJyZWNvdmVyZWQiID0gIk51bWJlcl9yZWNvdmVyZWQiKSkgJT4lDQogIG11dGF0ZShkYXRlID0gZGF0ZSAlPiUgbWR5KCkpICU+JQ0KICBncm91cF9ieShjb3VudHJ5LCBkYXRlKSAlPiUgYXMuZGF0YS5mcmFtZSgpIA0KVmlldyhkYXRhLnNhcnMpDQoNCg0KIyBBZGQgV29ybGQncyBTYXJzIGNhc2VzIA0Kd29ybGQuc2FycyA8LSBkYXRhLnNhcnMgJT4lIGdyb3VwX2J5KGRhdGUpICU+JSANCiAgc3VtbWFyaXNlKGNvdW50cnk9J1dvcmxkJywNCiAgICAgICAgICAgIGNvbmZpcm1lZCA9IHN1bShjb25maXJtZWQsIG5hLnJtPVQpLA0KICAgICAgICAgICAgZGVhdGhzID0gc3VtKGRlYXRocywgbmEucm09VCksDQogICAgICAgICAgICByZWNvdmVyZWQgPSBzdW0ocmVjb3ZlcmVkLCBuYS5ybT1UKSkNCg0KZGF0YS5zYXJzICU8PiUgcmJpbmQod29ybGQuc2FycykNCmRhdGEuc2FycyAlPD4lIG11dGF0ZShjdXJyZW50LmNvbmZpcm1lZCA9IGNvbmZpcm1lZCAtIGRlYXRocyAtIHJlY292ZXJlZCkNCiNWaWV3KHdvcmxkLnNhcnMpIA0KI1ZpZXcoZGF0YS5zYXJzKSANCmBgYA0KDQpgYGB7cn0NCiNyYXRlDQpkYXRhLnNhcnMgJTw+JSBhcnJhbmdlKGNvdW50cnksIGRhdGUpDQpuIDwtIG5yb3coZGF0YS5zYXJzKQ0KZGF5MS5zYXJzIDwtIG1pbihkYXRhLnNhcnMkZGF0ZSkNCmRhdGEuc2FycyAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MS5zYXJzLCBOQSwgY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCwgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLnNhcnMsIE5BLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShkYXRlID09IGRheTEuc2FycywgTkEsIHJlY292ZXJlZCAtIGxhZyhyZWNvdmVyZWQsIG49MSkpKQ0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShuZXcucmVjb3ZlcmVkIDwgMCwgMCwgbmV3LnJlY292ZXJlZCkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGRlYXRocyBhbmQgcmVjb3ZlcmVkIGNhc2VzDQpkYXRhLnNhcnMgJTw+JSBtdXRhdGUocmF0ZS51cHBlciA9ICgxMDAgKiBkZWF0aHMgLyAoZGVhdGhzICsgcmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLnVwcGVyID0gaWZlbHNlKGlzLm5hbihyYXRlLnVwcGVyKSwgMCwgcmF0ZS51cHBlcikpDQoNCiMjIGxvd2VyIGJvdW5kOiBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGNvbmZpcm1lZCBjYXNlcw0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKHJhdGUubG93ZXIgPSAoMTAwICogZGVhdGhzIC8gY29uZmlybWVkKSAlPiUgcm91bmQoMSksDQogICAgICAgICAgICAgICAgIHJhdGUubG93ZXIgPSBpZmVsc2UoaXMubmFuKHJhdGUubG93ZXIpLCAwLCByYXRlLmxvd2VyKSkNCg0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGRlYXRoL3JlY292ZXJlZCBvbiBldmVyeSBzaW5nbGUgZGF5DQpkYXRhLnNhcnMgJTw+JSBtdXRhdGUocmF0ZS5kYWlseSA9ICgxMDAgKiBuZXcuZGVhdGhzIC8gKG5ldy5kZWF0aHMgKyBuZXcucmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLmRhaWx5ID0gaWZlbHNlKGlzLm5hbihyYXRlLmRhaWx5KSwgMCwgcmF0ZS5kYWlseSkpDQoNClZpZXcoZGF0YS5zYXJzKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGEuc2Fycy5sb25nIDwtIGRhdGEuc2FycyAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwgcmVjb3ZlcmVkLCBkZWF0aHMpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSwgZGF0ZSkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGEuc2Fycy5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXJlZD0nUmVjb3ZlcmVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdEZWF0aHMnKSkNClZpZXcoZGF0YS5zYXJzLmxvbmcpDQoNCiMgV29ybGQgc2FycycgbG9uZyBkYXRhIA0Kd29ybGQuc2Fycy5sb25nIDwtIGRhdGEuc2Fycy5sb25nICU+JQ0KICBmaWx0ZXIoY291bnRyeSA9PSAiV29ybGQiKQ0KVmlldyh3b3JsZC5zYXJzLmxvbmcpDQoNCmcgPC0gZ2dwbG90KHdvcmxkLnNhcnMubG9uZywgYWVzKGRhdGUsIGNvdW50LCBjb2xvciA9IHR5cGUpKSArDQogIGdlb21fbGluZSgpKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBDYXNlcyBXb3JsZHdpZGU6IFNBUnMiKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpDQpnDQoNCmdseS5nIDwtIGdncGxvdGx5KGcpDQpnbHkuZw0KZ2x5LnBsb3QyDQpgYGANCmBgYHtyfQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMjIEN1cnJlbnQgQ29uZmlybWVkIENhc2VzDQpkYXRhLnNhcnMud29ybGQgPC0gZGF0YS5zYXJzICU+JSBmaWx0ZXIoY291bnRyeT09J1dvcmxkJykNClZpZXcoZGF0YS5zYXJzLndvcmxkKQ0KbiA8LSBucm93KGRhdGEuc2Fycy53b3JsZCkNClZpZXcoZGF0YS5zYXJzLndvcmxkKQ0KcGxvdDEgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1jdXJyZW50LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0N1cnJlbnQgQ29uZmlybWVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0RhaWx5IE5ldyBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCmBgYA0KYGBge3J9DQojIyBhIHNjYXR0ZXIgcGxvdCB3aXRoIGEgc21vb3RoZWQgbGluZSBhbmQgdmVydGljYWwgeC1heGlzIGxhYmVscw0KcGxvdDEgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1kZWF0aHMpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgRGVhdGhzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDMgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3Q0IDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LnJlY292ZXJlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBSZWNvdmVyZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQojIyBzaG93IGZvdXIgcGxvdHMgdG9nZXRoZXIsIHdpdGggMiBwbG90cyBpbiBlYWNoIHJvdw0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgcGxvdDMsIHBsb3Q0LCBucm93PTIpDQpgYGANCg0KYGBge3J9DQojZGZfdGhhaSA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZF90aGFpbGFuZF91cGRhdGVkIikpDQojZGZfdGhhaSA8LSBhcy5kYXRhLmZyYW1lKGRmX3RoYWkpDQoNCmRmX3RoYWkgPC0gcmVhZF9jc3YoIkQ6LzRELTIvUHJvamVjdCAyL0RhdGEvY292aWRfdGhhaWxhbmRfdXBkYXRlZC5jc3YiKQ0KU3lzLnNldGxvY2FsZSgiTENfQ1RZUEUiLCAiVGhhaSIpDQpvcHRpb25zKGVuY29kaW5nID0gIlVURi04IikNCiNWaWV3KGRmX3RoYWkpDQoNCmRhdGVzLnRoIDwtIGRmX3RoYWlbLDJdJT4lIG1keSgpDQpyYW5nZShkYXRlcy50aCkNCm1pbi5kYXRlLnRoIDwtIG1pbihkYXRlcy50aCkNCm1heC5kYXRlLnRoIDwtIG1heChkYXRlcy50aCkNCm1pbi5kYXRlLnR4dC50aCA8LSBtaW4uZGF0ZS50aCAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQptYXguZGF0ZS50eHQudGggPC0gbWF4LmRhdGUudGggJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KDQpkZl90aGFpJGFubm91bmNlX2RhdGUgPC0gbWR5KGRmX3RoYWkkYW5ub3VuY2VfZGF0ZSkNCmRmX3RoYWkkbm90aWZpY2F0aW9uX2RhdGUgPC0gbWR5KGRmX3RoYWkkbm90aWZpY2F0aW9uX2RhdGUpDQpkZl90aGFpDQoNCmRmX3RoYWkgPC0gZGZfdGhhaSAlPiUgc2VsZWN0KCFOby4pICU+JSANCiAgZ3JvdXBfYnkoYW5ub3VuY2VfZGF0ZSkNCmRmX3RoYWkNCmBgYA0KYGBge3J9DQojIFRvdGFsIGNvbmZpcm1lZCBjYXNlcyBpbiBUaGFpbGFuZA0KZGF0YS50aGFpLmNvdW50IDwtIGRmX3RoYWkgJT4lDQogIHNlbGVjdChhbm5vdW5jZV9kYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvbWZpcm1lZCA9IG4oKSkgICU+JSBhcy5kYXRhLmZyYW1lKCkNCmRhdGEudGhhaS5jb3VudA0KDQpkYXRhLnRoYWkuY291bnQkY3VtdWxhdGl2ZV9jb25maXJtZWQgPC0gY3Vtc3VtKGRhdGEudGhhaS5jb3VudFssIDJdKQ0KZGF0YS50aGFpLmNvdW50IA0KDQpnLnRoIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQpICsgDQogIGdlb21fbGluZShhZXMoeCA9IGFubm91bmNlX2RhdGUsIHkgPSBjdW11bGF0aXZlX2NvbmZpcm1lZCkpICsNCiAgbGFicyh0aXRsZSA9ICJUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSkiKSArDQogIHhsYWIoIkRhdGUgKEFubm91bmNlIERhdGUpIikgKyANCiAgeWxhYigiQ29uZmlybWVkIENhc2VzIikNCg0KZy50aA0KDQpnbHkudGggPC0gZ2dwbG90bHkoZy50aCkNCmdseS50aA0KYGBgDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSBzZXggKGdlbmRlcikNCmRhdGEudGhhaS5nZW5kZXIgPC0gZGZfdGhhaSAlPiUNCiAgZ3JvdXBfYnkoc2V4KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnQgPSAoY291bnQgLyBzdW0oY291bnQpICogMTAwKSAlPiUgcm91bmQoMikpICU+JQ0KICAjbXV0YXRlKHBvcyA9IGN1bXN1bShwZXJjZW50KSAtIDAuNSpwZXJjZW50KSAlPiUNCiAgYXJyYW5nZShkZXNjKHBlcmNlbnQpKQ0KZGF0YS50aGFpLmdlbmRlcg0KDQpkYXRhLnRoYWkuZ2VuZGVyJHNleCA8LSBmYWN0b3IoZGF0YS50aGFpLmdlbmRlciRzZXgsIGxldmVscyA9IGFzLmNoYXJhY3RlcihkYXRhLnRoYWkuZ2VuZGVyJHNleCkpDQpkYXRhLnRoYWkuZ2VuZGVyJHNleA0KDQpnLnRoLmdlbmRlciA8LSBkYXRhLnRoYWkuZ2VuZGVyICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50LCBmaWxsID0gc2V4KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArDQogIGNvb3JkX3BvbGFyKCJ5IikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHBlcmNlbnQsICIlIikpLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSA1LCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpIA0KDQpnLnRoLmdlbmRlcg0KDQpgYGANCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IGFnZQ0KZGF0YS50aGFpLmFnZSA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShhZ2UpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoY291bnQpKQ0KZGF0YS50aGFpLmFnZQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSBuYXRpb25hbGl0eQ0KZGF0YS50aGFpLm5hdGlvbmFsaXR5IDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KG5hdGlvbmFsaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvdW50KSkNCmRhdGEudGhhaS5uYXRpb25hbGl0eQ0KYGBgDQoNCmBgYHtyfQ0KIyBEYXRhIGluIFVTDQpkZl91cyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZHVzIikpDQpkZl91cyA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzKQ0KDQpkZl91c19wb3AgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9wb3B1bGF0aW9uIikpIA0KZGZfdXNfcG9wIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfcG9wKQ0KDQpkZl91c19nZW5kZXIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9nZW5kZXIiKSkgDQpkZl91c19nZW5kZXIgPC0gYXMuZGF0YS5mcmFtZShkZl91c19nZW5kZXIpDQoNCmRmX3VzX2V0aG5pYyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX2V0aG5pYyIpKSANCmRmX3VzX2V0aG5pYyA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX2V0aG5pYykNCg0KZGZfdXNfbG9ja2Rvd24gPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9sb2NrZG93biIpKSANCmRmX3VzX2xvY2tkb3duIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfbG9ja2Rvd24pDQoNCmRmX3VzX2hlYWx0aCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX2hlYWx0aCIpKSANCmRmX3VzX2hlYWx0aCA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX2hlYWx0aCkNCg0KZGZfdXNfdGVzdGluZyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX3Rlc3RpbmciKSkgDQpkZl91c190ZXN0aW5nIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfdGVzdGluZykNCg0KZGZfdXNfbGF0bG9uZyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSB1c19sYXRsb25nIikpDQpkZl91c19sYXRsb25nIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfbGF0bG9uZykNCg0KI1ZpZXcoZGZfdXNfbGF0bG9uZykNCg0KZGF0YS51cyA8LSBkZl91cyAlPiUgc2VsZWN0KGRhdGUsIHN0YXRlLCBjYXNlcywgZGVhdGhzKSAlPiUNCiAgbXV0YXRlKGRhdGUgPSBkYXRlICU+JSBtZHkoKSkgJT4lDQogIHJlbmFtZSgiY29uZmlybWVkIiA9ICJjYXNlcyIpDQojZGF0YS51cw0KDQpkYXRlcy51cyA8LSBkYXRhLnVzWywxXQ0KcmFuZ2UoZGF0ZXMudXMpDQptaW4uZGF0ZS51cyA8LSBtaW4oZGF0ZXMudXMpDQptYXguZGF0ZS51cyA8LSBtYXgoZGF0ZXMudXMpDQptaW4uZGF0ZS50eHQudXMgPC0gbWluLmRhdGUudXMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0LnVzIDwtIG1heC5kYXRlLnVzICU+JSBmb3JtYXQoJyVkICViICVZJykNCmRheTEudXMgPC0gbWluKGRhdGEudXMkZGF0ZSkNCg0KZGF0YS51cy50b3RhbCA8LSBkYXRhLnVzICU+JSBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKHN0YXRlPSdVUycsDQogICAgICAgICAgICBjb25maXJtZWQgPSBzdW0oY29uZmlybWVkLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIGRlYXRocyA9IHN1bShkZWF0aHMsIG5hLnJtPVQpKQ0KI1ZpZXcoZGF0YS51cy50b3RhbCkNCmRhdGEudXMgJTw+JSByYmluZChkYXRhLnVzLnRvdGFsKQ0KVmlldyhkYXRhLnVzKQ0KDQpkYXRhLnVzLmxvbmcgPC0gZGF0YS51cyAlPiUgDQogIGdhdGhlcihrZXkgPSB0eXBlLCB2YWx1ZSA9IGNvdW50LCAtYyhkYXRlLCBzdGF0ZSkpIA0KI2RhdGEudXMubG9uZw0KDQp1cy50b3RhbCA8LSBkYXRhLnVzLnRvdGFsICU+JQ0KICBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEsIDAsIGNvbmZpcm1lZCAtIGxhZyhjb25maXJtZWQsIG49MSkpLA0KICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCAwLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSkNCg0KdXMudG90YWwgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShuZXcuY29uZmlybWVkIDwgMCwgMCwgbmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UobmV3LmRlYXRocyA8IDAsIDAsIG5ldy5kZWF0aHMpKQ0KdXMudG90YWwNCg0KdXMudG90YWwubG9uZyA8LSB1cy50b3RhbCAlPiUgDQogIGdhdGhlcihrZXkgPSB0eXBlLCB2YWx1ZSA9IGNvdW50LCAtYyhkYXRlLCBzdGF0ZSkpDQoNClZpZXcodXMudG90YWwubG9uZykNCg0KDQpgYGANCg0KYGBge3J9DQpWaWV3KHVzLnRvdGFsLmxvbmcpDQoNCmcxIDwtIGRhdGEudXMubG9uZyAlPiUgZmlsdGVyKHN0YXRlID09ICJVUyIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY291bnQsIGNvbG9yID0gdHlwZSkpICsgDQogIGdlb21fbGluZSgpICsgDQogIGxhYnModGl0bGUgPSBwYXN0ZTAoIkN1bXVsYXRpdmUgY2FzZXMgaW4gVVMgOiAiLCBtaW4uZGF0ZS50eHQudXMsICctJywgbWF4LmRhdGUudHh0LnVzLCAiIChMb2cgU2NhbGUpIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQoNCmcyIDwtIGRhdGEudXMubG9uZyAlPiUgZmlsdGVyKHN0YXRlID09ICJVUyIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY291bnQpKSArDQogIGdlb21fYXJlYShhZXMoZmlsbD10eXBlKSwgYWxwaGE9MC41KSArDQogIGxhYnModGl0bGUgPSBwYXN0ZTAoIkN1bXVsYXRpdmUgY2FzZXMgaW4gVVMgOiAiLCBtaW4uZGF0ZS50eHQudXMsICctJywgbWF4LmRhdGUudHh0LnVzLCAiIChMb2cgU2NhbGUpIikpICArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoJ3JlZCcsICdibGFjaycpKSsNCiAgeWxhYigiIikNCg0KZzMgPC0gdXMudG90YWwubG9uZyAlPiUNCiAgZmlsdGVyKHR5cGUgJWluJSBjKCJuZXcuY29uZmlybWVkIikpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY291bnQsIGNvbG9yID0gdHlwZSkpICsgDQogIGdlb21fbGluZSgpICsgDQogIGxhYnModGl0bGUgPSBwYXN0ZTAoIkRhaWx5IGNvbmZpcm1lZCBjYXNlcyBpbiBVUyA6ICIsIG1pbi5kYXRlLnR4dC51cywgJy0nLCBtYXguZGF0ZS50eHQudXMpKSArDQogIHhsYWIoIkRhdGUiKSArDQogIHlsYWIoIkNvbmZpcm1lZCBjYXNlcyIpDQoNCiNnMQ0KI2cyDQojZzMNCg0KI2dseS51czEgPC0gZ2dwbG90bHkoZzEpDQpnbHkudXMyIDwtIGdncGxvdGx5KGcyKQ0KZ2x5LnVzMyA8LSBnZ3Bsb3RseShnMykNCg0KI2dseS51czENCmdseS51czINCmdseS51czMNCmBgYA0KDQoNCg0KYGBge3J9DQoNCmRhdGEudXMgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBjb25maXJtZWQgLSBsYWcoY29uZmlybWVkLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSkNCg0KZGF0YS51cyAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKG5ldy5jb25maXJtZWQgPCAwLCAwLCBuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShuZXcuZGVhdGhzIDwgMCwgMCwgbmV3LmRlYXRocykpDQpWaWV3KGRhdGEudXMpDQoNCiNkYXRhLnVzLmRhaWx5IDwtIGRhdGEudXMgJT4lIGZpbHRlcihzdGF0ZSA9PSAiVVMiKQ0KI1ZpZXcoZGF0YS51cy5kYWlseSkNCg0KZGF0YS51cy5wb3AgPC0gZGZfdXNfcG9wICU+JSBzZWxlY3QoU3RhdGUsIFBvcHVsYXRpb24pICU+JQ0KICByZW5hbWUoInN0YXRlIiA9ICJTdGF0ZSIpIA0KI2RhdGEudXMucG9wDQoNCmRhdGEudXMuZ2VuZGVyIDwtIGRmX3VzX2dlbmRlciAlPiUgc2VsZWN0KFN0YXRlLCBNYWxlLCBGZW1hbGUpICU+JQ0KICByZW5hbWUoInN0YXRlIiA9ICJTdGF0ZSIpIA0KZGF0YS51cy5nZW5kZXINCg0KZGF0YS51cy5sYXRlc3QgPC0gZGF0YS51cyAlPiUNCiAgZmlsdGVyKGRhdGUgPT0gbWF4LmRhdGUudXMpICU+JSANCiAgbWVyZ2UoZGF0YS51cy5wb3AsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUKSAlPiUNCiAgbWVyZ2UoZGF0YS51cy5nZW5kZXIsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUKQ0KI1ZpZXcoZGF0YS51cy5sYXRlc3QpDQoNCmRhdGEudXMubGF0ZXN0JFBvcHVsYXRpb25bZGF0YS51cy5sYXRlc3Qkc3RhdGUgPT0gIlVTIl0gPC0gc3VtKGRhdGEudXMucG9wJFBvcHVsYXRpb24pDQpkYXRhLnVzLmxhdGVzdCAlPD4lIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhkZXNjKGNvbmZpcm1lZCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlybWVkLnJhdGUgPSAoMTAwICogY29uZmlybWVkIC8gUG9wdWxhdGlvbikgJT4lIHJvdW5kKDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGgucmF0ZSA9ICgxMDAgKiBkZWF0aHMgLyBjb25maXJtZWQpICU+JSByb3VuZCgyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE1hbGUuY29uZmlybWVkID0gKE1hbGUgKiBjb25maXJtZWQpICU+JSByb3VuZCgwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZlbWFsZS5jb25maXJtZWQgPSBGZW1hbGUgKiBjb25maXJtZWQgJT4lIHJvdW5kKDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgTWFsZS5kZWF0aHMgPSBNYWxlICogZGVhdGhzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRmVtYWxlLmRlYXRocyA9IEZlbWFsZSAqIGRlYXRocykgJT4lDQogIGFycmFuZ2UocmFua2luZykgDQoNCnRvcC51cyA8LSBkYXRhLnVzLmxhdGVzdFssMV0NCnRvcC51cw0KDQoNCmRhdGEudXMubGF0ZXN0DQoNCg0KYGBgDQoNCmBgYHtyfQ0KIyBMaXN0IG9mIHRvcCAyMCBzdGF0ZQ0KayA8LSAyMA0KZGF0YS51cy50b3AgPC0gZGF0YS51cy5sYXRlc3QgJT4lDQogIGZpbHRlcihyYW5raW5nIDw9IGsrMSkgJT4lIA0KICBhcnJhbmdlKHJhbmtpbmcpIA0KDQp1cy5zdGF0ZS50b3AgPC0gZGF0YS51cy50b3AgJT4lIHB1bGwoc3RhdGUpICU+JSBhcy5jaGFyYWN0ZXIoKQ0KdXMuc3RhdGUudG9wICAlPiUgc2V0ZGlmZignVVMnKSAlPiUgcHJpbnQoKQ0KDQojIGNvbmZpcm1lZCByYXRlICYgZGVhdGggcmF0ZSBvZiB0b3AgMjAgc3RhdGUNCmcucmF0ZSA8LSBkYXRhLnVzLmxhdGVzdCAlPiUgZmlsdGVyKHN0YXRlICVpbiUgdXMuc3RhdGUudG9wICYgc3RhdGUgIT0gIlVTIikgJT4lDQogIHNlbGVjdChzdGF0ZSwgY29uZmlybWVkLnJhdGUsIGRlYXRoLnJhdGUsIHJhbmtpbmcpICU+JQ0KICBnYXRoZXIoa2V5ID0gVHlwZSwgdmFsdWUgPSBQZXJjZW50LCAtYyhzdGF0ZSwgcmFua2luZykpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihzdGF0ZSwgLWRlc2MocmFua2luZykpLCB5PVBlcmNlbnQsIGZpbGwgPSBQZXJjZW50KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI2ViYmM2MiIsIGhpZ2ggPSAiI2I0MjAwNiIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1QZXJjZW50LCB5PVBlcmNlbnQpLCBzaXplPTMsIHZqdXN0PTApICsNCiAgeGxhYignJykgKyB5bGFiKCcnKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdDb25maXJtZWQgUmF0ZSAmIERlYXRoIFJhdGUgb2YgVG9wIDIwIFN0YXRlIGluIFVTJykpICsNCiAgI3NjYWxlX2ZpbGxfY29udGludW91cyhuYW1lPSdTdGF0ZScsIGxhYmVscz1hZXMoUGVyY2VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMyksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH5UeXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykgDQoNCmcucmF0ZQ0KYGBgDQoNCg0KYGBge3J9DQp1cy5jb25maXJtZWQubnVtIDwtIGRhdGEudXMudG9wJGNvbmZpcm1lZFtkYXRhLnVzLnRvcCRzdGF0ZSA9PSAiVVMiXSAlPiUgYXMubnVtZXJpYygpDQp1cy5jb25maXJtZWQubnVtDQoNCmRhdGEudXMudG9wICU8PiUgbXV0YXRlKGNvbmZpcm1lZC5wZXIudXMgPSAoY29uZmlybWVkICogMTAwIC8gdXMuY29uZmlybWVkLm51bSkgJT4lIHJvdW5kKDEpKQ0KZGF0YS51cy50b3ANCg0KZy51cy50b3AxIDwtIGRhdGEudXMudG9wICU+JQ0KICBmaWx0ZXIoc3RhdGUgIT0gIlVTIikgJT4lDQogIHNlbGVjdChzdGF0ZSwgY29uZmlybWVkLCBjb25maXJtZWQucGVyLnVzLCByYW5raW5nKSAlPiUNCiAgZ2F0aGVyKGtleSA9IFR5cGUsIHZhbHVlID0gY291bnQsIC1jKHN0YXRlLCByYW5raW5nLCBjb25maXJtZWQucGVyLnVzKSkgJT4lDQogIA0KICBnZ3Bsb3QoYWVzKGZpbGwgPSBjb3VudCwgeSA9IGNvdW50LCB4ID0gcmVvcmRlcihzdGF0ZSwgLWRlc2MocmFua2luZykpKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHRpdGxlID0gIjIwIHN0YXRlIGluIFVTIHdpdGggbW9zdCBjb25maXJtZWQgY2FzZXMiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIiNlYmJjNjIiLCBoaWdoID0gIiNiNDIwMDYiKSArDQogIHhsYWIoIiIpICsgDQogIHlsYWIoIkNvbmZpcm1lZCBDYXNlcyIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoY29uZmlybWVkLnBlci51cywgIiUiKSksIHNpemU9Mywgdmp1c3Q9LTAuNSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMC41KSkrDQogIHRoZW1lKA0KICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCg0KZy51cy50b3AyIDwtIGRhdGEudXMudG9wICU+JQ0KICBmaWx0ZXIoc3RhdGUgIT0gIlVTIikgJT4lDQogIHNlbGVjdChzdGF0ZSwgTWFsZS5jb25maXJtZWQsIEZlbWFsZS5jb25maXJtZWQsIE1hbGUuZGVhdGhzLCBGZW1hbGUuZGVhdGhzLCByYW5raW5nKSAlPiUNCiAgZ2F0aGVyKGtleSA9IFR5cGUsIHZhbHVlID0gY291bnQsIC1jKHN0YXRlLCByYW5raW5nKSkgJT4lDQogIA0KICBnZ3Bsb3QoYWVzKGZpbGwgPSBUeXBlLCB5ID0gY291bnQsIHggPSByZW9yZGVyKHN0YXRlLCAtZGVzYyhyYW5raW5nKSkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiMjAgbW9zdCBjb25maXJtZWQgY2FzZXMncyBzdGF0ZSBpbiBVUyAoRGl2aWRlZCBieSBnZW5kZXIpIikgKw0KICB4bGFiKCIiKSArIA0KICB5bGFiKCJDYXNlcyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSx2anVzdCA9IDEpKSArDQogIHRoZW1lKA0KICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCg0KZy51cy50b3AzIDwtICBkYXRhLnVzLnRvcCAlPiUNCiAgZmlsdGVyKHN0YXRlICE9ICJVUyIpICU+JQ0KICBzZWxlY3Qoc3RhdGUsIGNvbmZpcm1lZC5wZXIudXMsIHJhbmtpbmcpICU+JQ0KICBnYXRoZXIoa2V5ID0gVHlwZSwgdmFsdWUgPSBjb3VudCwgLWMoc3RhdGUsIHJhbmtpbmcpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBjb25maXJtZWQucGVyLnVzLCBmaWxsID0gY29uZmlybWVkLnBlci51cykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcigieSIpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50LCAiJSIpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKQ0KDQpnLnVzLnRvcDENCg0KZ2x5LnVzLnRvcDEgPC0gZ2dwbG90bHkoZy51cy50b3AxKQ0KZ2x5LnVzLnRvcDENCg0KZ2x5LnVzLnRvcDIgPC0gZ2dwbG90bHkoZy51cy50b3AyKQ0KZ2x5LnVzLnRvcDINCg0KI2dseS51cy50b3AzIDwtIGdncGxvdGx5KGcudXMudG9wMykNCiNnbHkudXMudG9wMw0KDQpgYGANCg0KYGBge3J9DQpkYXRhLnVzLmxhdGxvbmcgPC0gZGZfdXNfbGF0bG9uZyAlPiUNCiAgcmVuYW1lKCJsYXQiID0gImxhdGl0dWRlIiwgImxvbmciID0gImxvbmdpdHVkZSIpDQoNCiNsYWJlbF93b3JsZCA8LSBsYXQubG9uZyANCiNsYWJlbF93b3JsZCRhdmdfdGVtcCA8LSBhcy5udW1lcmljKGxhYmVsX3dvcmxkWywgbmFtZXMobGFiZWxfd29ybGQpICVpbiUgYygiYXZnX3RlbXAiKV0pDQojbGFiZWxfd29ybGQgPC0gbGFiZWxfd29ybGQgJT4lICANCiMgIG11dGF0ZSh0eHQ9cGFzdGUwKCc8Yj4nLHJhbmtpbmcsICc8L2I+JywNCiMgICAgICAgICAgICAgICAgICAgICc8YnIvPicsJzxiPicsY291bnRyeSwgJzwvYj4nLA0KIyAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIlRlbXBlcmF0dXJlOiAgIixhdmdfdGVtcCwgJyDCsEMnLA0KIyAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIkNvbmZpcm1lZDogICIsIGNvbmZpcm1lZCwgDQojICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLCAiRGVhdGhzOiAiLCBkZWF0aHMsDQojICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLCAiUmVjb3ZlcmVkOiAiLCByZWNvdmVyZWQNCiMgICAgICAgICAgICAgICAgICAgICkpIA0KDQojbGFiZWxfd29ybGQkdHh0IDwtIGxhYmVsX3dvcmxkJHR4dCAlPiUgbGFwcGx5KGh0bWx0b29sczo6SFRNTCkNCiNsYWJlbF93b3JsZA0KDQptYXAudXMgPC0gZGF0YS51cy5sYXRlc3QgJT4lDQogIHNlbGVjdChzdGF0ZSwgY29uZmlybWVkLCBjb25maXJtZWQucmF0ZSwgZGVhdGhzLCBkZWF0aC5yYXRlLCByYW5raW5nKSAlPiUNCiAgbWVyZ2UoZGF0YS51cy5sYXRsb25nKSAlPiUNCiAgbXV0YXRlKCkNCm1hcC51cw0KYGBgDQoNCg0KDQpgYGB7cn0NCm1lcmdjb3VudHJ5ID0gZnVuY3Rpb24oZGF0YTEsZGF0YTIpew0KICBkYXRhIDwtIG1lcmdlKHggPSBkYXRhMSwgeSA9IGRhdGEyLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSANCiAgcmV0dXJuKGRhdGEpDQp9DQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3AsIHkgPSBkZl9nZHAyMDE5LCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUgDQogIHNlbGVjdCgtYyhjb2RlLHJhbmssbmV3LmNvbmZpcm1lZCxuZXcuZGVhdGhzLGN1cnJlbnQuY29uZmlybWVkLHBvcHVsYXRpb24pKSAlPiUgDQogIHJlbmFtZShHRFA9IkdEUCAobWlsbGlvbnMgb2YgVVMgZG9sbGFycykiKQ0KDQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3Aud29ybGQsIHkgPSBkZl9oZWFsdCwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lDQogIHJlbmFtZShoZWFsdGhjYXJlPSJoZWFsdGhDYXJlSW5kZXgiKQ0KDQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3Aud29ybGQsIHkgPSBkZl9wb3JuaHViLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUNCiAgcmVuYW1lKFBvcm5odWIgPSAiUG9ybmh1YkluZGV4KCUpIikNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2NvdW50cnkoZGF0YS50b3Aud29ybGQsIGRmX3RlbXApDQppbmRleCA8LSBpcy5uYShkYXRhLnRvcC53b3JsZCkNCmRhdGEudG9wLndvcmxkW2luZGV4XSA8LSAwDQpkYXRhLnRvcC53b3JsZA0KI1ZpZXcoZGF0YS50b3Aud29ybGQpDQoNCm5vcm1hbGl6ZSA9IGZ1bmN0aW9uKGRhdGEpew0KICAjcmV0dXJuICgoZGF0YSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpLyhtYXgoZGF0YSxuYS5ybSA9IFRSVUUpIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkpDQogIHogPC0gc2NhbGUoZGF0YSk7DQogIHRhbmgoei8yKQ0KfQ0KDQpub3JtX2RhdGEgPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGRhdGEudG9wLndvcmxkWywyOjEyXSwyLG5vcm1hbGl6ZSkpDQpjb3JyX2RhdGEgPC0gbm9ybV9kYXRhDQpub3JtX2RhdGEkY291bnRyeSA8LSBjKCJBcmdlbnRpbmEiLCJCYW5nbGFkZXNoIiwiQnJhemlsIiwiQ2hpbGUiLCJDb2xvbWJpYSIsIkZyYW5jZSIsIkdlcm1hbnkiLCJJbmRpYSIsIklyYW4iLCJJdGFseSIsIk1leGljbyIsIlBha2lzdGFuIiwiUGVydSIsIlJ1c3NpYSIsInNhdWRpIEFyYWJpYSIsIlNvdXRoIEFmcmljYSIsIlNwYWluIiwiVHVya2V5IiwiVW5pdGVkIEtpbmdkb20iLCJVUyIpDQpWaWV3KG5vcm1fZGF0YSkNCm5vcm1fZGF0YV9wbG90IDwtIHNlbGVjdChub3JtX2RhdGEsImNvdW50cnkiLCJjb25maXJtLnJhdGUiLCJkZWF0aC5yYXRlIiwicmVjb3Zlci5yYXRlIiwiaGVhbHRoY2FyZSIsIlBvcm5odWIiLCJHRFAiLCJhdmdfdGVtcCIpDQpub3JtX2RhdGFfcGxvdCAlPD4lIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnkpKQ0KbGV2ZWxfb3JkZXIgPC0gZmFjdG9yKG5vcm1fZGF0YV9wbG90JHR5cGUsIA0KICAgICAgICAgICAgICAgICAgICAgIGxldmVsID0gYygiR0RQIiwiYXZnX3RlbXAiLCJoZWFsdGhjYXJlIiwicmVjb3Zlci5yYXRlIiwiZGVhdGgucmF0ZSIsImNvbmZpcm0ucmF0ZSIsIlBvcm5odWIiKSkNCmdncGxvdChkYXRhID0gbm9ybV9kYXRhX3Bsb3QsIGFlcyh4PWNvdW50cnksIHk9bGV2ZWxfb3JkZXIsIGZpbGw9Y291bnQpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInBpbmsiLCBoaWdoID0gImJsdWUiKSArDQogIHhsYWIoIiIpICsNCiAgeWxhYigiIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCx2anVzdCA9IDEpKSsNCiAgdGhlbWUoDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICNsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KYGBgDQoNCg0KYGBge3J9DQojY29ycmVsYXRpb24NCmNvcnJfZGF0YSAlPD4lIHNlbGVjdChjKEdEUCxjb25maXJtLnJhdGUsZGVhdGgucmF0ZSxyZWNvdmVyLnJhdGUsIGhlYWx0aGNhcmUsIGF2Z190ZW1wLCBQb3JuaHViKSkNCmhlYWQoY29ycl9kYXRhKQ0KY29yKGNvcnJfZGF0YSkNCmdnY29ycnBsb3QoY29yKGNvcnJfZGF0YSksaGMub3JkZXIgPSBUUlVFLA0KICAgICAgICAgICBvdXRsaW5lLmNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzZEOUVDMSIsIndoaXRlIiwiI0U0NjcyNiIpLA0KICAgICAgICAgICBsYWIgPSBUUlVFKQ0KYGBgDQoNCg0KDQo=